webengine/osswebengine/WebKit/WebView/WebFrameView.mm
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  *
       
     8  * 1.  Redistributions of source code must retain the above copyright
       
     9  *     notice, this list of conditions and the following disclaimer. 
       
    10  * 2.  Redistributions in binary form must reproduce the above copyright
       
    11  *     notice, this list of conditions and the following disclaimer in the
       
    12  *     documentation and/or other materials provided with the distribution. 
       
    13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
       
    14  *     its contributors may be used to endorse or promote products derived
       
    15  *     from this software without specific prior written permission. 
       
    16  *
       
    17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
       
    18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
    19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
       
    20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
       
    21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
       
    22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
       
    23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
       
    24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
       
    26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    27  */
       
    28 
       
    29 #import "WebFrameView.h"
       
    30 
       
    31 #import "WebClipView.h"
       
    32 #import "WebDataSource.h"
       
    33 #import "WebDocument.h"
       
    34 #import "WebDynamicScrollBarsView.h"
       
    35 #import "WebFrame.h"
       
    36 #import "WebFrameInternal.h"
       
    37 #import "WebFrameBridge.h"
       
    38 #import "WebFrameViewInternal.h"
       
    39 #import "WebFrameViewPrivate.h"
       
    40 #import "WebHistoryItemInternal.h"
       
    41 #import "WebHTMLViewPrivate.h"
       
    42 #import "WebKeyGenerator.h"
       
    43 #import "WebKitErrorsPrivate.h"
       
    44 #import "WebKitStatisticsPrivate.h"
       
    45 #import "WebKitVersionChecks.h"
       
    46 #import "WebNSDictionaryExtras.h"
       
    47 #import "WebNSObjectExtras.h"
       
    48 #import "WebNSPasteboardExtras.h"
       
    49 #import "WebNSViewExtras.h"
       
    50 #import "WebNSWindowExtras.h"
       
    51 #import "WebPDFView.h"
       
    52 #import "WebPreferenceKeysPrivate.h"
       
    53 #import "WebSystemInterface.h"
       
    54 #import "WebViewFactory.h"
       
    55 #import "WebViewInternal.h"
       
    56 #import "WebViewPrivate.h"
       
    57 #import <Foundation/NSURLRequest.h>
       
    58 #import <JavaScriptCore/Assertions.h>
       
    59 #import <WebCore/DragController.h>
       
    60 #import <WebCore/Frame.h>
       
    61 #import <WebCore/FrameView.h>
       
    62 #import <WebCore/HistoryItem.h>
       
    63 #import <WebCore/Page.h>
       
    64 #import <WebCore/ThreadCheck.h>
       
    65 #import <WebCore/WebCoreFrameView.h>
       
    66 #import <WebCore/WebCoreView.h>
       
    67 #import <WebKitSystemInterface.h>
       
    68 
       
    69 using namespace WebCore;
       
    70 
       
    71 @interface NSWindow (WindowPrivate)
       
    72 - (BOOL)_needsToResetDragMargins;
       
    73 - (void)_setNeedsToResetDragMargins:(BOOL)s;
       
    74 @end
       
    75 
       
    76 @interface NSClipView (AppKitSecretsIKnow)
       
    77 - (BOOL)_scrollTo:(const NSPoint *)newOrigin animate:(BOOL)animate; // need the boolean result from this method
       
    78 @end
       
    79 
       
    80 enum {
       
    81     SpaceKey = 0x0020
       
    82 };
       
    83 
       
    84 @interface WebFrameView (WebFrameViewFileInternal) <WebCoreBridgeHolder>
       
    85 - (float)_verticalKeyboardScrollDistance;
       
    86 - (WebCoreFrameBridge *) webCoreBridge;
       
    87 @end
       
    88 
       
    89 @interface WebFrameViewPrivate : NSObject {
       
    90 @public
       
    91     WebFrame *webFrame;
       
    92     WebDynamicScrollBarsView *frameScrollView;
       
    93     
       
    94     // These margin values are used to temporarily hold the margins of a frame until
       
    95     // we have the appropriate document view type.
       
    96     int marginWidth;
       
    97     int marginHeight;
       
    98 }
       
    99 @end
       
   100 
       
   101 @implementation WebFrameViewPrivate
       
   102 
       
   103 - init
       
   104 {
       
   105     [super init];
       
   106     
       
   107     marginWidth = -1;
       
   108     marginHeight = -1;
       
   109     
       
   110     return self;
       
   111 }
       
   112 
       
   113 - (void)dealloc
       
   114 {
       
   115     [frameScrollView release];
       
   116     [super dealloc];
       
   117 }
       
   118 
       
   119 @end
       
   120 
       
   121 @implementation WebFrameView (WebFrameViewFileInternal)
       
   122 
       
   123 - (float)_verticalKeyboardScrollDistance
       
   124 {
       
   125     // Arrow keys scroll the same distance that clicking the scroll arrow does.
       
   126     return [[self _scrollView] verticalLineScroll];
       
   127 }
       
   128 
       
   129 - (WebCoreFrameBridge *) webCoreBridge
       
   130 {
       
   131     return [_private->webFrame _bridge];
       
   132 }
       
   133 
       
   134 @end
       
   135 
       
   136 @implementation WebFrameView (WebInternal)
       
   137 
       
   138 // Note that the WebVew is not retained.
       
   139 - (WebView *)_webView
       
   140 {
       
   141     return [_private->webFrame webView];
       
   142 }
       
   143 
       
   144 - (void)_setMarginWidth:(int)w
       
   145 {
       
   146     _private->marginWidth = w;
       
   147 }
       
   148 
       
   149 - (int)_marginWidth
       
   150 {
       
   151     return _private->marginWidth;
       
   152 }
       
   153 
       
   154 - (void)_setMarginHeight:(int)h
       
   155 {
       
   156     _private->marginHeight = h;
       
   157 }
       
   158 
       
   159 - (int)_marginHeight
       
   160 {
       
   161     return _private->marginHeight;
       
   162 }
       
   163 
       
   164 - (void)_setDocumentView:(NSView <WebDocumentView> *)view
       
   165 {
       
   166     WebDynamicScrollBarsView *sv = [self _scrollView];
       
   167     core([self _webView])->dragController()->setDidInitiateDrag(false);
       
   168     
       
   169     [sv setSuppressLayout:YES];
       
   170     
       
   171     // If the old view is the first responder, transfer first responder status to the new view as 
       
   172     // a convenience and so that we don't leave the window pointing to a view that's no longer in it.
       
   173     NSWindow *window = [sv window];
       
   174     NSResponder *firstResponder = [window firstResponder];
       
   175     bool makeNewViewFirstResponder = [firstResponder isKindOfClass:[NSView class]] && [(NSView *)firstResponder isDescendantOf:[sv documentView]];
       
   176 
       
   177     // Suppress the resetting of drag margins since we know we can't affect them.
       
   178     BOOL resetDragMargins = [window _needsToResetDragMargins];
       
   179     [window _setNeedsToResetDragMargins:NO];
       
   180     [sv setDocumentView:view];
       
   181     [window _setNeedsToResetDragMargins:resetDragMargins];
       
   182 
       
   183     if (makeNewViewFirstResponder)
       
   184         [window makeFirstResponder:view];
       
   185     [sv setSuppressLayout:NO];
       
   186 }
       
   187 
       
   188 -(NSView <WebDocumentView> *)_makeDocumentViewForDataSource:(WebDataSource *)dataSource
       
   189 {
       
   190     NSString* MIMEType = [[dataSource response] MIMEType];
       
   191     if (!MIMEType)
       
   192         MIMEType = @"text/html";
       
   193     Class viewClass = [[self class] _viewClassForMIMEType:MIMEType];
       
   194     NSView <WebDocumentView> *documentView;
       
   195     if (viewClass) {
       
   196         // If the dataSource's representation has already been created, and it is also the
       
   197         // same class as the desired documentView, then use it as the documentView instead
       
   198         // of creating another one (Radar 4340787).
       
   199         id <WebDocumentRepresentation> dataSourceRepresentation = [dataSource representation];
       
   200         if (dataSourceRepresentation && [dataSourceRepresentation class] == viewClass)
       
   201             documentView = (NSView <WebDocumentView> *)[dataSourceRepresentation retain];
       
   202         else
       
   203             documentView = [[viewClass alloc] initWithFrame:[self bounds]];
       
   204     } else
       
   205         documentView = nil;
       
   206     
       
   207     [self _setDocumentView:documentView];
       
   208     [documentView release];
       
   209     
       
   210     return documentView;
       
   211 }
       
   212 
       
   213 - (void)_setWebFrame:(WebFrame *)webFrame
       
   214 {
       
   215     if (!webFrame) {
       
   216         NSView *docV = [self documentView];
       
   217         if ([docV respondsToSelector:@selector(close)])
       
   218             [docV performSelector:@selector(close)];
       
   219     }
       
   220 
       
   221     // Not retained because the WebView owns the WebFrame, which owns the WebFrameView.
       
   222     _private->webFrame = webFrame;    
       
   223 }
       
   224 
       
   225 - (WebDynamicScrollBarsView *)_scrollView
       
   226 {
       
   227     // this can be called by [super dealloc] when cleaning up the keyview loop,
       
   228     // after _private has been nilled out.
       
   229     if (_private == nil) {
       
   230         return nil;
       
   231     }
       
   232     return _private->frameScrollView;
       
   233 }
       
   234 
       
   235 - (float)_verticalPageScrollDistance
       
   236 {
       
   237     float overlap = [self _verticalKeyboardScrollDistance];
       
   238     float height = [[self _contentView] bounds].size.height;
       
   239     return (height < overlap) ? height / 2 : height - overlap;
       
   240 }
       
   241 
       
   242 static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class objCClass, NSArray *supportTypes)
       
   243 {
       
   244     NSEnumerator *enumerator = [supportTypes objectEnumerator];
       
   245     ASSERT(enumerator != nil);
       
   246     NSString *mime = nil;
       
   247     while ((mime = [enumerator nextObject]) != nil) {
       
   248         // Don't clobber previously-registered classes.
       
   249         if ([allTypes objectForKey:mime] == nil)
       
   250             [allTypes setObject:objCClass forKey:mime];
       
   251     }
       
   252 }
       
   253 
       
   254 + (NSMutableDictionary *)_viewTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission
       
   255 {
       
   256     static NSMutableDictionary *viewTypes = nil;
       
   257     static BOOL addedImageTypes = NO;
       
   258     
       
   259     if (!viewTypes) {
       
   260         viewTypes = [[NSMutableDictionary alloc] init];
       
   261         addTypesFromClass(viewTypes, [WebHTMLView class], [WebHTMLView supportedNonImageMIMETypes]);
       
   262 
       
   263         // Since this is a "secret default" we don't both registering it.
       
   264         BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"];
       
   265         if (!omitPDFSupport)
       
   266             addTypesFromClass(viewTypes, [WebPDFView class], [WebPDFView supportedMIMETypes]);
       
   267     }
       
   268     
       
   269     if (!addedImageTypes && !allowImageTypeOmission) {
       
   270         addTypesFromClass(viewTypes, [WebHTMLView class], [WebHTMLView supportedImageMIMETypes]);
       
   271         addedImageTypes = YES;
       
   272     }
       
   273     
       
   274     return viewTypes;
       
   275 }
       
   276 
       
   277 + (BOOL)_canShowMIMETypeAsHTML:(NSString *)MIMEType
       
   278 {
       
   279     return [[[self _viewTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType] isSubclassOfClass:[WebHTMLView class]];
       
   280 }
       
   281 
       
   282 + (Class)_viewClassForMIMEType:(NSString *)MIMEType
       
   283 {
       
   284     Class viewClass;
       
   285     return [WebView _viewClass:&viewClass andRepresentationClass:nil forMIMEType:MIMEType] ? viewClass : nil;
       
   286 }
       
   287 
       
   288 @end
       
   289 
       
   290 @implementation WebFrameView
       
   291 
       
   292 - initWithFrame:(NSRect)frame
       
   293 {
       
   294     self = [super initWithFrame:frame];
       
   295     if (!self)
       
   296         return nil;
       
   297  
       
   298     static bool didFirstTimeInitialization;
       
   299     if (!didFirstTimeInitialization) {
       
   300         didFirstTimeInitialization = true;
       
   301         InitWebCoreSystemInterface();
       
   302         
       
   303         // Need to tell WebCore what function to call for the 
       
   304         // "History Item has Changed" notification
       
   305         // Note: We also do this in WebHistoryItem's init method
       
   306         WebCore::notifyHistoryItemChanged = WKNotifyHistoryItemChanged;
       
   307 
       
   308         [WebViewFactory createSharedFactory];
       
   309         [WebKeyGenerator createSharedGenerator];
       
   310 
       
   311         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
       
   312         
       
   313         // CoreGraphics deferred updates are disabled if WebKitEnableCoalescedUpdatesPreferenceKey is set
       
   314         // to NO, or has no value.  For compatibility with Mac OS X 10.4.6, deferred updates are OFF by
       
   315         // default.
       
   316         if (![defaults boolForKey:WebKitEnableDeferredUpdatesPreferenceKey])
       
   317             WKDisableCGDeferredUpdates();
       
   318 
       
   319         if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_MAIN_THREAD_EXCEPTIONS))
       
   320             setDefaultThreadViolationBehavior(LogOnFirstThreadViolation);
       
   321     }
       
   322     
       
   323     _private = [[WebFrameViewPrivate alloc] init];
       
   324 
       
   325     WebDynamicScrollBarsView *scrollView  = [[WebDynamicScrollBarsView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, frame.size.width, frame.size.height)];
       
   326     _private->frameScrollView = scrollView;
       
   327     [scrollView setContentView:[[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]];
       
   328     [scrollView setDrawsBackground:NO];
       
   329     [scrollView setHasVerticalScroller:NO];
       
   330     [scrollView setHasHorizontalScroller:NO];
       
   331     [scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
       
   332     [scrollView setLineScroll:40.0f];
       
   333     [self addSubview:scrollView];
       
   334     // don't call our overridden version here; we need to make the standard NSView link between us
       
   335     // and our subview so that previousKeyView and previousValidKeyView work as expected. This works
       
   336     // together with our becomeFirstResponder and setNextKeyView overrides.
       
   337     [super setNextKeyView:scrollView];
       
   338     
       
   339     ++WebFrameViewCount;
       
   340     
       
   341     return self;
       
   342 }
       
   343 
       
   344 - (void)dealloc 
       
   345 {
       
   346     --WebFrameViewCount;
       
   347     
       
   348     [_private release];
       
   349     _private = nil;
       
   350     
       
   351     [super dealloc];
       
   352 }
       
   353 
       
   354 - (void)finalize 
       
   355 {
       
   356     --WebFrameViewCount;
       
   357 
       
   358     [super finalize];
       
   359 }
       
   360 
       
   361 - (WebFrame *)webFrame
       
   362 {
       
   363     return _private->webFrame;
       
   364 }
       
   365 
       
   366 - (void)setAllowsScrolling:(BOOL)flag
       
   367 {
       
   368     WebDynamicScrollBarsView *scrollView = [self _scrollView];
       
   369     [scrollView setAllowsScrolling:flag];
       
   370     WebCore::Frame *frame = core([self webFrame]);
       
   371     if (WebCore::FrameView *view = frame? frame->view() : 0) {
       
   372         view->setHScrollbarMode((WebCore::ScrollbarMode)[scrollView horizontalScrollingMode]);
       
   373         view->setVScrollbarMode((WebCore::ScrollbarMode)[scrollView verticalScrollingMode]);
       
   374     }
       
   375 }
       
   376 
       
   377 - (BOOL)allowsScrolling
       
   378 {
       
   379     return [[self _scrollView] allowsScrolling];
       
   380 }
       
   381 
       
   382 - (NSView <WebDocumentView> *)documentView
       
   383 {
       
   384     return [[self _scrollView] documentView];
       
   385 }
       
   386 
       
   387 - (BOOL)acceptsFirstResponder
       
   388 {
       
   389     // We always accept first responder; this matches OS X 10.2 WebKit
       
   390     // behavior (see 3469791).
       
   391     return YES;
       
   392 }
       
   393 
       
   394 - (BOOL)becomeFirstResponder
       
   395 {
       
   396     // This works together with setNextKeyView to splice the WebFrameView into
       
   397     // the key loop similar to the way NSScrollView does this. Note that
       
   398     // WebView has similar code.
       
   399     
       
   400     // If the scrollView won't accept first-responderness now, then we just become
       
   401     // the first responder ourself like a normal view. This lets us be the first 
       
   402     // responder in cases where no page has yet been loaded (see 3469791).
       
   403     if ([[self _scrollView] acceptsFirstResponder]) {
       
   404         NSWindow *window = [self window];
       
   405         if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
       
   406             NSView *previousValidKeyView = [self previousValidKeyView];
       
   407             // If we couldn't find a previous valid key view, ask the webview. This handles frameset
       
   408             // cases like 3748628. Note that previousValidKeyView should never be self but can be
       
   409             // due to AppKit oddness (mentioned in 3748628).
       
   410             if (previousValidKeyView == nil || previousValidKeyView == self) {
       
   411                 previousValidKeyView = [[[self webFrame] webView] previousValidKeyView];
       
   412             }
       
   413             // I don't know if the following cases ever occur anymore, but I'm leaving in the old test for
       
   414             // now to avoid causing trouble just before shipping Tiger.
       
   415             ASSERT((previousValidKeyView != self) && (previousValidKeyView != [self _scrollView]));
       
   416             if ((previousValidKeyView != self) && (previousValidKeyView != [self _scrollView])) {
       
   417                 [window makeFirstResponder:previousValidKeyView];
       
   418             }
       
   419         } else {
       
   420             [window makeFirstResponder:[self _scrollView]];
       
   421         }
       
   422     }    
       
   423     
       
   424     return YES;
       
   425 }
       
   426 
       
   427 - (void)setNextKeyView:(NSView *)aView
       
   428 {
       
   429     // This works together with becomeFirstResponder to splice the WebFrameView into
       
   430     // the key loop similar to the way NSScrollView does this. Note that
       
   431     // WebView has very similar code.
       
   432     if ([self _scrollView] != nil) {
       
   433         [[self _scrollView] setNextKeyView:aView];
       
   434     } else {
       
   435         [super setNextKeyView:aView];
       
   436     }
       
   437 }
       
   438 
       
   439 - (BOOL)isOpaque
       
   440 {
       
   441     return [[self _webView] drawsBackground];
       
   442 }
       
   443 
       
   444 - (void)drawRect:(NSRect)rect
       
   445 {
       
   446     if ([self documentView] == nil) {
       
   447         // Need to paint ourselves if there's no documentView to do it instead.
       
   448         if ([[self _webView] drawsBackground]) {
       
   449             [[[self _webView] backgroundColor] set];
       
   450             NSRectFill(rect);
       
   451         }
       
   452     } else {
       
   453 #ifndef NDEBUG
       
   454         if ([[self _scrollView] drawsBackground]) {
       
   455             [[NSColor cyanColor] set];
       
   456             NSRectFill(rect);
       
   457         }
       
   458 #endif
       
   459     }
       
   460 }
       
   461 
       
   462 - (void)setFrameSize:(NSSize)size
       
   463 {
       
   464     if (!NSEqualSizes(size, [self frame].size) && [[[self webFrame] webView] drawsBackground]) {
       
   465         [[self _scrollView] setDrawsBackground:YES];
       
   466     }
       
   467     [super setFrameSize:size];
       
   468 }
       
   469 
       
   470 - (WebFrameBridge *)_bridge
       
   471 {
       
   472     return [[self webFrame] _bridge];
       
   473 }
       
   474 
       
   475 - (BOOL)_scrollOverflowInDirection:(WebScrollDirection)direction granularity:(WebScrollGranularity)granularity
       
   476 {
       
   477     // scrolling overflows is only applicable if we're dealing with an WebHTMLView
       
   478     return ([[self documentView] isKindOfClass:[WebHTMLView class]] && [[self _bridge] scrollOverflowInDirection:direction granularity:granularity]);
       
   479 }
       
   480 
       
   481 - (void)scrollToBeginningOfDocument:(id)sender
       
   482 {
       
   483     if (![self _scrollOverflowInDirection:WebScrollUp granularity:WebScrollDocument]) {
       
   484 
       
   485         if (![self _hasScrollBars]) {
       
   486             [[self _largestChildWithScrollBars] scrollToBeginningOfDocument:sender];
       
   487             return;
       
   488         }
       
   489 
       
   490         [[self _contentView] scrollPoint:[[[self _scrollView] documentView] frame].origin];
       
   491     }
       
   492 }
       
   493 
       
   494 - (void)scrollToEndOfDocument:(id)sender
       
   495 {
       
   496     if (![self _scrollOverflowInDirection:WebScrollDown granularity:WebScrollDocument]) {
       
   497 
       
   498         if (![self _hasScrollBars]) {
       
   499             [[self _largestChildWithScrollBars] scrollToEndOfDocument:sender];
       
   500             return;
       
   501         }
       
   502         
       
   503         NSRect frame = [[[self _scrollView] documentView] frame];
       
   504         [[self _contentView] scrollPoint:NSMakePoint(frame.origin.x, NSMaxY(frame))];
       
   505     }
       
   506 }
       
   507 
       
   508 - (void)_goBack
       
   509 {
       
   510     [[self _webView] goBack];
       
   511 }
       
   512 
       
   513 - (void)_goForward
       
   514 {
       
   515     [[self _webView] goForward];
       
   516 }
       
   517 
       
   518 - (BOOL)_scrollVerticallyBy:(float)delta
       
   519 {
       
   520     // This method uses the secret method _scrollTo on NSClipView.
       
   521     // It does that because it needs to know definitively whether scrolling was
       
   522     // done or not to help implement the "scroll parent if we are at the limit" feature.
       
   523     // In the presence of smooth scrolling, there's no easy way to tell if the method
       
   524     // did any scrolling or not with the public API.
       
   525     NSPoint point = [[self _contentView] bounds].origin;
       
   526     point.y += delta;
       
   527     return [[self _contentView] _scrollTo:&point animate:YES];
       
   528 }
       
   529 
       
   530 - (BOOL)_scrollHorizontallyBy:(float)delta
       
   531 {
       
   532     NSPoint point = [[self _contentView] bounds].origin;
       
   533     point.x += delta;
       
   534     return [[self _contentView] _scrollTo:&point animate:YES];
       
   535 }
       
   536 
       
   537 - (float)_horizontalKeyboardScrollDistance
       
   538 {
       
   539     // Arrow keys scroll the same distance that clicking the scroll arrow does.
       
   540     return [[self _scrollView] horizontalLineScroll];
       
   541 }
       
   542 
       
   543 - (float)_horizontalPageScrollDistance
       
   544 {
       
   545     float overlap = [self _horizontalKeyboardScrollDistance];
       
   546     float width = [[self _contentView] bounds].size.width;
       
   547     return (width < overlap) ? width / 2 : width - overlap;
       
   548 }
       
   549 
       
   550 - (BOOL)_pageVertically:(BOOL)up
       
   551 {
       
   552     if ([self _scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollPage])
       
   553         return YES;
       
   554     
       
   555     if (![self _hasScrollBars])
       
   556         return [[self _largestChildWithScrollBars] _pageVertically:up];
       
   557 
       
   558     float delta = [self _verticalPageScrollDistance];
       
   559     return [self _scrollVerticallyBy:up ? -delta : delta];
       
   560 }
       
   561 
       
   562 - (BOOL)_pageHorizontally:(BOOL)left
       
   563 {
       
   564     if ([self _scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollPage])
       
   565         return YES;
       
   566 
       
   567     if (![self _hasScrollBars])
       
   568         return [[self _largestChildWithScrollBars] _pageHorizontally:left];
       
   569     
       
   570     float delta = [self _horizontalPageScrollDistance];
       
   571     return [self _scrollHorizontallyBy:left ? -delta : delta];
       
   572 }
       
   573 
       
   574 - (BOOL)_scrollLineVertically:(BOOL)up
       
   575 {
       
   576     if ([self _scrollOverflowInDirection:up ? WebScrollUp : WebScrollDown granularity:WebScrollLine])
       
   577         return YES;
       
   578 
       
   579     if (![self _hasScrollBars])
       
   580         return [[self _largestChildWithScrollBars] _scrollLineVertically:up];
       
   581     
       
   582     float delta = [self _verticalKeyboardScrollDistance];
       
   583     return [self _scrollVerticallyBy:up ? -delta : delta];
       
   584 }
       
   585 
       
   586 - (BOOL)_scrollLineHorizontally:(BOOL)left
       
   587 {
       
   588     if ([self _scrollOverflowInDirection:left ? WebScrollLeft : WebScrollRight granularity:WebScrollLine])
       
   589         return YES;
       
   590 
       
   591     if (![self _hasScrollBars])
       
   592         return [[self _largestChildWithScrollBars] _scrollLineHorizontally:left];
       
   593 
       
   594     float delta = [self _horizontalKeyboardScrollDistance];
       
   595     return [self _scrollHorizontallyBy:left ? -delta : delta];
       
   596 }
       
   597 
       
   598 - (void)scrollPageUp:(id)sender
       
   599 {
       
   600     if (![self _pageVertically:YES]) {
       
   601         // If we were already at the top, tell the next responder to scroll if it can.
       
   602         [[self nextResponder] tryToPerform:@selector(scrollPageUp:) with:sender];
       
   603     }
       
   604 }
       
   605 
       
   606 - (void)scrollPageDown:(id)sender
       
   607 {
       
   608     if (![self _pageVertically:NO]) {
       
   609         // If we were already at the bottom, tell the next responder to scroll if it can.
       
   610         [[self nextResponder] tryToPerform:@selector(scrollPageDown:) with:sender];
       
   611     }
       
   612 }
       
   613 
       
   614 - (void)scrollLineUp:(id)sender
       
   615 {
       
   616     [self _scrollLineVertically:YES];
       
   617 }
       
   618 
       
   619 - (void)scrollLineDown:(id)sender
       
   620 {
       
   621     [self _scrollLineVertically:NO];
       
   622 }
       
   623 
       
   624 - (BOOL)_firstResponderIsFormControl
       
   625 {
       
   626     NSResponder *firstResponder = [[self window] firstResponder];
       
   627     
       
   628     // WebHTMLView is an NSControl subclass these days, but it's not a form control
       
   629     if ([firstResponder isKindOfClass:[WebHTMLView class]]) {
       
   630         return NO;
       
   631     }
       
   632     return [firstResponder isKindOfClass:[NSControl class]];
       
   633 }
       
   634 
       
   635 - (void)keyDown:(NSEvent *)event
       
   636 {
       
   637     NSString *characters = [event characters];
       
   638     int index, count;
       
   639     BOOL callSuper = YES;
       
   640     BOOL maintainsBackForwardList = core([self webFrame])->page()->backForwardList()->enabled() ? YES : NO;
       
   641     
       
   642     count = [characters length];
       
   643     for (index = 0; index < count; ++index) {
       
   644         switch ([characters characterAtIndex:index]) {
       
   645             case NSDeleteCharacter:
       
   646                 if (!maintainsBackForwardList) {
       
   647                     callSuper = YES;
       
   648                     break;
       
   649                 }
       
   650                 // This odd behavior matches some existing browsers,
       
   651                 // including Windows IE
       
   652                 if ([event modifierFlags] & NSShiftKeyMask) {
       
   653                     [self _goForward];
       
   654                 } else {
       
   655                     [self _goBack];
       
   656                 }
       
   657                 callSuper = NO;
       
   658                 break;
       
   659             case SpaceKey:
       
   660                 // Checking for a control will allow events to percolate 
       
   661                 // correctly when the focus is on a form control and we
       
   662                 // are in full keyboard access mode.
       
   663                 if ((![self allowsScrolling] && ![self _largestChildWithScrollBars]) || [self _firstResponderIsFormControl]) {
       
   664                     callSuper = YES;
       
   665                     break;
       
   666                 }
       
   667                 if ([event modifierFlags] & NSShiftKeyMask) {
       
   668                     [self scrollPageUp:nil];
       
   669                 } else {
       
   670                     [self scrollPageDown:nil];
       
   671                 }
       
   672                 callSuper = NO;
       
   673                 break;
       
   674             case NSPageUpFunctionKey:
       
   675                 if (![self allowsScrolling] && ![self _largestChildWithScrollBars]) {
       
   676                     callSuper = YES;
       
   677                     break;
       
   678                 }
       
   679                 [self scrollPageUp:nil];
       
   680                 callSuper = NO;
       
   681                 break;
       
   682             case NSPageDownFunctionKey:
       
   683                 if (![self allowsScrolling] && ![self _largestChildWithScrollBars]) {
       
   684                     callSuper = YES;
       
   685                     break;
       
   686                 }
       
   687                 [self scrollPageDown:nil];
       
   688                 callSuper = NO;
       
   689                 break;
       
   690             case NSHomeFunctionKey:
       
   691                 if (![self allowsScrolling] && ![self _largestChildWithScrollBars]) {
       
   692                     callSuper = YES;
       
   693                     break;
       
   694                 }
       
   695                 [self scrollToBeginningOfDocument:nil];
       
   696                 callSuper = NO;
       
   697                 break;
       
   698             case NSEndFunctionKey:
       
   699                 if (![self allowsScrolling] && ![self _largestChildWithScrollBars]) {
       
   700                     callSuper = YES;
       
   701                     break;
       
   702                 }
       
   703                 [self scrollToEndOfDocument:nil];
       
   704                 callSuper = NO;
       
   705                 break;
       
   706             case NSUpArrowFunctionKey:
       
   707                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
       
   708                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
       
   709                     callSuper = YES;
       
   710                     break;
       
   711                 }
       
   712                 if ((![self allowsScrolling] && ![self _largestChildWithScrollBars]) ||
       
   713                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
       
   714                     // Let arrow keys go through to pop up buttons
       
   715                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
       
   716                     // pop-up menu should pop the menu
       
   717                     callSuper = YES;
       
   718                     break;
       
   719                 }
       
   720                 if ([event modifierFlags] & NSCommandKeyMask) {
       
   721                     [self scrollToBeginningOfDocument:nil];
       
   722                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
       
   723                     [self scrollPageUp:nil];
       
   724                 } else {
       
   725                     [self scrollLineUp:nil];
       
   726                 }
       
   727                 callSuper = NO;
       
   728                 break;
       
   729             case NSDownArrowFunctionKey:
       
   730                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
       
   731                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
       
   732                     callSuper = YES;
       
   733                     break;
       
   734                 }
       
   735                 if ((![self allowsScrolling] && ![self _largestChildWithScrollBars]) ||
       
   736                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
       
   737                     // Let arrow keys go through to pop up buttons
       
   738                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a 
       
   739                     // pop-up menu should pop the menu
       
   740                     callSuper = YES;
       
   741                     break;
       
   742                 }
       
   743                 if ([event modifierFlags] & NSCommandKeyMask) {
       
   744                     [self scrollToEndOfDocument:nil];
       
   745                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
       
   746                     [self scrollPageDown:nil];
       
   747                 } else {
       
   748                     [self scrollLineDown:nil];
       
   749                 }
       
   750                 callSuper = NO;
       
   751                 break;
       
   752             case NSLeftArrowFunctionKey:
       
   753                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
       
   754                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
       
   755                     callSuper = YES;
       
   756                     break;
       
   757                 }
       
   758                 // Check back/forward related keys.
       
   759                 if ([event modifierFlags] & NSCommandKeyMask) {
       
   760                     if (!maintainsBackForwardList) {
       
   761                         callSuper = YES;
       
   762                         break;
       
   763                     }
       
   764                     [self _goBack];
       
   765                 } else {
       
   766                     // Now check scrolling related keys.
       
   767                     if ((![self allowsScrolling] && ![self _largestChildWithScrollBars])) {
       
   768                         callSuper = YES;
       
   769                         break;
       
   770                     }
       
   771 
       
   772                     if ([event modifierFlags] & NSAlternateKeyMask) {
       
   773                         [self _pageHorizontally:YES];
       
   774                     } else {
       
   775                         [self _scrollLineHorizontally:YES];
       
   776                     }
       
   777                 }
       
   778                 callSuper = NO;
       
   779                 break;
       
   780             case NSRightArrowFunctionKey:
       
   781                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
       
   782                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
       
   783                     callSuper = YES;
       
   784                     break;
       
   785                 }
       
   786                 // Check back/forward related keys.
       
   787                 if ([event modifierFlags] & NSCommandKeyMask) {
       
   788                     if (!maintainsBackForwardList) {
       
   789                         callSuper = YES;
       
   790                         break;
       
   791                     }
       
   792                     [self _goForward];
       
   793                 } else {
       
   794                     // Now check scrolling related keys.
       
   795                     if ((![self allowsScrolling] && ![self _largestChildWithScrollBars])) {
       
   796                         callSuper = YES;
       
   797                         break;
       
   798                     }
       
   799 
       
   800                     if ([event modifierFlags] & NSAlternateKeyMask) {
       
   801                         [self _pageHorizontally:NO];
       
   802                     } else {
       
   803                         [self _scrollLineHorizontally:NO];
       
   804                     }
       
   805                 }
       
   806                 callSuper = NO;
       
   807                 break;
       
   808         }
       
   809     }
       
   810     
       
   811     if (callSuper) {
       
   812         [super keyDown:event];
       
   813     } else {
       
   814         // if we did something useful, get the cursor out of the way
       
   815         [NSCursor setHiddenUntilMouseMoves:YES];
       
   816     }
       
   817 }
       
   818 
       
   819 - (NSView *)_webcore_effectiveFirstResponder
       
   820 {
       
   821     NSView *view = [self documentView];
       
   822     return view ? [view _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
       
   823 }
       
   824 
       
   825 - (BOOL)canPrintHeadersAndFooters
       
   826 {
       
   827     NSView *documentView = [[self _scrollView] documentView];
       
   828     if ([documentView respondsToSelector:@selector(canPrintHeadersAndFooters)]) {
       
   829         return [(id)documentView canPrintHeadersAndFooters];
       
   830     }
       
   831     return NO;
       
   832 }
       
   833 
       
   834 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
       
   835 {
       
   836     NSView *documentView = [[self _scrollView] documentView];
       
   837     if (!documentView) {
       
   838         return nil;
       
   839     }
       
   840     if ([documentView respondsToSelector:@selector(printOperationWithPrintInfo:)]) {
       
   841         return [(id)documentView printOperationWithPrintInfo:printInfo];
       
   842     }
       
   843     return [NSPrintOperation printOperationWithView:documentView printInfo:printInfo];
       
   844 }
       
   845 
       
   846 - (BOOL)documentViewShouldHandlePrint
       
   847 {
       
   848     NSView *documentView = [[self _scrollView] documentView];
       
   849     if (documentView && [documentView respondsToSelector:@selector(documentViewShouldHandlePrint)])
       
   850         return [(id)documentView documentViewShouldHandlePrint];
       
   851     
       
   852     return NO;
       
   853 }
       
   854 
       
   855 - (void)printDocumentView
       
   856 {
       
   857     NSView *documentView = [[self _scrollView] documentView];
       
   858     if (documentView && [documentView respondsToSelector:@selector(printDocumentView)])
       
   859         [(id)documentView printDocumentView];
       
   860 }
       
   861 
       
   862 @end
       
   863 
       
   864 @implementation WebFrameView (WebPrivate)
       
   865 
       
   866 - (float)_area
       
   867 {
       
   868     NSRect frame = [self frame];
       
   869     return frame.size.height * frame.size.width;
       
   870 }
       
   871 
       
   872 - (BOOL)_hasScrollBars
       
   873 {
       
   874     NSScrollView *scrollView = [self _scrollView];
       
   875     return [scrollView hasHorizontalScroller] || [scrollView hasVerticalScroller];
       
   876 }
       
   877 
       
   878 - (WebFrameView *)_largestChildWithScrollBars
       
   879 {
       
   880     WebFrameView *largest = nil;
       
   881     NSArray *frameChildren = [[self webFrame] childFrames];
       
   882     
       
   883     unsigned i;
       
   884     for (i=0; i < [frameChildren count]; i++) {
       
   885         WebFrameView *childFrameView = [[frameChildren objectAtIndex:i] frameView];
       
   886         WebFrameView *scrollableFrameView = [childFrameView _hasScrollBars] ? childFrameView : [childFrameView _largestChildWithScrollBars];
       
   887         if (!scrollableFrameView)
       
   888             continue;
       
   889         
       
   890         // Some ads lurk in child frames of zero width and height, see radar 4406994. These don't count as scrollable.
       
   891         // Maybe someday we'll discover that this minimum area check should be larger, but this covers the known cases.
       
   892         float area = [scrollableFrameView _area];
       
   893         if (area < 1.0)
       
   894             continue;
       
   895         
       
   896         if (!largest || (area > [largest _area])) {
       
   897             largest = scrollableFrameView;
       
   898         }
       
   899     }
       
   900     
       
   901     return largest;
       
   902 }
       
   903 
       
   904 - (NSClipView *)_contentView
       
   905 {
       
   906     return [[self _scrollView] contentView];
       
   907 }
       
   908 
       
   909 - (Class)_customScrollViewClass
       
   910 {
       
   911     if ([_private->frameScrollView class] == [WebDynamicScrollBarsView class])
       
   912         return nil;
       
   913     return [_private->frameScrollView class];
       
   914 }
       
   915 
       
   916 - (void)_setCustomScrollViewClass:(Class)customClass
       
   917 {
       
   918     if (!customClass)
       
   919         customClass = [WebDynamicScrollBarsView class];
       
   920     ASSERT([customClass isSubclassOfClass:[WebDynamicScrollBarsView class]]);
       
   921     if (customClass == [_private->frameScrollView class])
       
   922         return;
       
   923     if ([customClass isSubclassOfClass:[WebDynamicScrollBarsView class]]) {
       
   924         WebDynamicScrollBarsView *oldScrollView = _private->frameScrollView; // already retained
       
   925         NSView <WebDocumentView> *documentView = [[self documentView] retain];
       
   926 
       
   927         WebDynamicScrollBarsView *scrollView  = [[customClass alloc] initWithFrame:[oldScrollView frame]];
       
   928         [scrollView setContentView:[[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]];
       
   929         [scrollView setDrawsBackground:[oldScrollView drawsBackground]];
       
   930         [scrollView setHasVerticalScroller:[oldScrollView hasVerticalScroller]];
       
   931         [scrollView setHasHorizontalScroller:[oldScrollView hasHorizontalScroller]];
       
   932         [scrollView setAutoresizingMask:[oldScrollView autoresizingMask]];
       
   933         [scrollView setLineScroll:[oldScrollView lineScroll]];
       
   934         [self addSubview:scrollView];
       
   935 
       
   936         // don't call our overridden version here; we need to make the standard NSView link between us
       
   937         // and our subview so that previousKeyView and previousValidKeyView work as expected. This works
       
   938         // together with our becomeFirstResponder and setNextKeyView overrides.
       
   939         [super setNextKeyView:scrollView];
       
   940 
       
   941         _private->frameScrollView = scrollView;
       
   942 
       
   943         [self _setDocumentView:documentView];
       
   944         [[self _bridge] installInFrame:scrollView];
       
   945 
       
   946         [oldScrollView removeFromSuperview];
       
   947         [oldScrollView release];
       
   948         [documentView release];
       
   949     }
       
   950 }
       
   951 
       
   952 @end