Home | History | Annotate | Download | only in WebView
      1 /*
      2  * Copyright (C) 2005, 2006, 2007, 2008 Apple 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 "WebDataSourcePrivate.h"
     33 #import "WebDocument.h"
     34 #import "WebDynamicScrollBarsViewInternal.h"
     35 #import "WebFrame.h"
     36 #import "WebFrameInternal.h"
     37 #import "WebFrameViewInternal.h"
     38 #import "WebFrameViewPrivate.h"
     39 #import "WebHistoryItemInternal.h"
     40 #import "WebHTMLViewPrivate.h"
     41 #import "WebKeyGenerator.h"
     42 #import "WebKitErrorsPrivate.h"
     43 #import "WebKitStatisticsPrivate.h"
     44 #import "WebKitVersionChecks.h"
     45 #import "WebNSDictionaryExtras.h"
     46 #import "WebNSObjectExtras.h"
     47 #import "WebNSPasteboardExtras.h"
     48 #import "WebNSViewExtras.h"
     49 #import "WebNSWindowExtras.h"
     50 #import "WebPDFView.h"
     51 #import "WebPreferenceKeysPrivate.h"
     52 #import "WebResourceInternal.h"
     53 #import "WebSystemInterface.h"
     54 #import "WebViewFactory.h"
     55 #import "WebViewInternal.h"
     56 #import "WebViewPrivate.h"
     57 #import <Foundation/NSURLRequest.h>
     58 #import <WebCore/BackForwardListImpl.h>
     59 #import <WebCore/DragController.h>
     60 #import <WebCore/EventHandler.h>
     61 #import <WebCore/Frame.h>
     62 #import <WebCore/FrameView.h>
     63 #import <WebCore/HistoryItem.h>
     64 #import <WebCore/Page.h>
     65 #import <WebCore/RenderPart.h>
     66 #import <WebCore/ThreadCheck.h>
     67 #import <WebCore/WebCoreFrameView.h>
     68 #import <WebCore/WebCoreView.h>
     69 #import <WebKitSystemInterface.h>
     70 #import <wtf/Assertions.h>
     71 
     72 using namespace WebCore;
     73 
     74 @interface NSWindow (WindowPrivate)
     75 - (BOOL)_needsToResetDragMargins;
     76 - (void)_setNeedsToResetDragMargins:(BOOL)s;
     77 @end
     78 
     79 @interface NSClipView (AppKitSecretsIKnow)
     80 - (BOOL)_scrollTo:(const NSPoint *)newOrigin animate:(BOOL)animate; // need the boolean result from this method
     81 @end
     82 
     83 enum {
     84     SpaceKey = 0x0020
     85 };
     86 
     87 @interface WebFrameView (WebFrameViewFileInternal) <WebCoreFrameView>
     88 - (float)_verticalKeyboardScrollDistance;
     89 @end
     90 
     91 @interface WebFrameViewPrivate : NSObject {
     92 @public
     93     WebFrame *webFrame;
     94     WebDynamicScrollBarsView *frameScrollView;
     95     BOOL includedInWebKitStatistics;
     96 }
     97 @end
     98 
     99 @implementation WebFrameViewPrivate
    100 
    101 - (void)dealloc
    102 {
    103     [frameScrollView release];
    104     [super dealloc];
    105 }
    106 
    107 @end
    108 
    109 @implementation WebFrameView (WebFrameViewFileInternal)
    110 
    111 - (float)_verticalKeyboardScrollDistance
    112 {
    113     // Arrow keys scroll the same distance that clicking the scroll arrow does.
    114     return [[self _scrollView] verticalLineScroll];
    115 }
    116 
    117 - (Frame*)_web_frame
    118 {
    119     return core(_private->webFrame);
    120 }
    121 
    122 @end
    123 
    124 @implementation WebFrameView (WebInternal)
    125 
    126 // Note that the WebVew is not retained.
    127 - (WebView *)_webView
    128 {
    129     return [_private->webFrame webView];
    130 }
    131 
    132 - (void)_setDocumentView:(NSView <WebDocumentView> *)view
    133 {
    134     WebDynamicScrollBarsView *sv = [self _scrollView];
    135     core([self _webView])->dragController()->setDidInitiateDrag(false);
    136 
    137     [sv setSuppressLayout:YES];
    138 
    139     // If the old view is the first responder, transfer first responder status to the new view as
    140     // a convenience and so that we don't leave the window pointing to a view that's no longer in it.
    141     NSWindow *window = [sv window];
    142     NSResponder *firstResponder = [window firstResponder];
    143     bool makeNewViewFirstResponder = [firstResponder isKindOfClass:[NSView class]] && [(NSView *)firstResponder isDescendantOf:[sv documentView]];
    144 
    145     // Suppress the resetting of drag margins since we know we can't affect them.
    146     BOOL resetDragMargins = [window _needsToResetDragMargins];
    147     [window _setNeedsToResetDragMargins:NO];
    148     [sv setDocumentView:view];
    149     [window _setNeedsToResetDragMargins:resetDragMargins];
    150 
    151     if (makeNewViewFirstResponder)
    152         [window makeFirstResponder:view];
    153     [sv setSuppressLayout:NO];
    154 }
    155 
    156 -(NSView <WebDocumentView> *)_makeDocumentViewForDataSource:(WebDataSource *)dataSource
    157 {
    158     NSString* MIMEType = [dataSource _responseMIMEType];
    159     if (!MIMEType)
    160         MIMEType = @"text/html";
    161     Class viewClass = [self _viewClassForMIMEType:MIMEType];
    162     NSView <WebDocumentView> *documentView;
    163     if (viewClass) {
    164         // If the dataSource's representation has already been created, and it is also the
    165         // same class as the desired documentView, then use it as the documentView instead
    166         // of creating another one (Radar 4340787).
    167         id <WebDocumentRepresentation> dataSourceRepresentation = [dataSource representation];
    168         if (dataSourceRepresentation && [dataSourceRepresentation class] == viewClass)
    169             documentView = (NSView <WebDocumentView> *)[dataSourceRepresentation retain];
    170         else
    171             documentView = [[viewClass alloc] initWithFrame:[self bounds]];
    172     } else
    173         documentView = nil;
    174 
    175     [self _setDocumentView:documentView];
    176     [documentView release];
    177 
    178     return documentView;
    179 }
    180 
    181 - (void)_setWebFrame:(WebFrame *)webFrame
    182 {
    183     if (!webFrame) {
    184         NSView *docV = [self documentView];
    185         if ([docV respondsToSelector:@selector(close)])
    186             [docV performSelector:@selector(close)];
    187     }
    188 
    189     // Not retained because the WebView owns the WebFrame, which owns the WebFrameView.
    190     _private->webFrame = webFrame;
    191 
    192     if (!_private->includedInWebKitStatistics && [webFrame _isIncludedInWebKitStatistics]) {
    193         _private->includedInWebKitStatistics = YES;
    194         ++WebFrameViewCount;
    195     }
    196 }
    197 
    198 - (WebDynamicScrollBarsView *)_scrollView
    199 {
    200     // This can be called by [super dealloc] when cleaning up the key view loop,
    201     // after _private has been nilled out.
    202     if (_private == nil)
    203         return nil;
    204     return _private->frameScrollView;
    205 }
    206 
    207 - (float)_verticalPageScrollDistance
    208 {
    209     float height = [[self _contentView] bounds].size.height;
    210     return max<float>(height * Scrollbar::minFractionToStepWhenPaging(), height - Scrollbar::maxOverlapBetweenPages());
    211 }
    212 
    213 static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class objCClass, NSArray *supportTypes)
    214 {
    215     NSEnumerator *enumerator = [supportTypes objectEnumerator];
    216     ASSERT(enumerator != nil);
    217     NSString *mime = nil;
    218     while ((mime = [enumerator nextObject]) != nil) {
    219         // Don't clobber previously-registered classes.
    220         if ([allTypes objectForKey:mime] == nil)
    221             [allTypes setObject:objCClass forKey:mime];
    222     }
    223 }
    224 
    225 + (NSMutableDictionary *)_viewTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission
    226 {
    227     static NSMutableDictionary *viewTypes = nil;
    228     static BOOL addedImageTypes = NO;
    229 
    230     if (!viewTypes) {
    231         viewTypes = [[NSMutableDictionary alloc] init];
    232         addTypesFromClass(viewTypes, [WebHTMLView class], [WebHTMLView supportedNonImageMIMETypes]);
    233 
    234         // Since this is a "secret default" we don't bother registering it.
    235         BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"];
    236         if (!omitPDFSupport)
    237             addTypesFromClass(viewTypes, [WebPDFView class], [WebPDFView supportedMIMETypes]);
    238     }
    239 
    240     if (!addedImageTypes && !allowImageTypeOmission) {
    241         addTypesFromClass(viewTypes, [WebHTMLView class], [WebHTMLView supportedImageMIMETypes]);
    242         addedImageTypes = YES;
    243     }
    244 
    245     return viewTypes;
    246 }
    247 
    248 + (BOOL)_canShowMIMETypeAsHTML:(NSString *)MIMEType
    249 {
    250     return [[[self _viewTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType] isSubclassOfClass:[WebHTMLView class]];
    251 }
    252 
    253 + (Class)_viewClassForMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins
    254 {
    255     Class viewClass;
    256     return [WebView _viewClass:&viewClass andRepresentationClass:nil forMIMEType:MIMEType allowingPlugins:allowPlugins] ? viewClass : nil;
    257 }
    258 
    259 - (Class)_viewClassForMIMEType:(NSString *)MIMEType
    260 {
    261     return [[self class] _viewClassForMIMEType:MIMEType allowingPlugins:[[[self _webView] preferences] arePlugInsEnabled]];
    262 }
    263 
    264 - (void)_install
    265 {
    266     ASSERT(_private->webFrame);
    267     ASSERT(_private->frameScrollView);
    268 
    269     Frame* frame = core(_private->webFrame);
    270 
    271     ASSERT(frame);
    272     ASSERT(frame->page());
    273 
    274     // If this isn't the main frame, it must have an owner element set, or it
    275     // won't ever get installed in the view hierarchy.
    276     ASSERT(frame == frame->page()->mainFrame() || frame->ownerElement());
    277 
    278     FrameView* view = frame->view();
    279 
    280     view->setPlatformWidget(_private->frameScrollView);
    281 
    282     // FIXME: Frame tries to do this too. Is this code needed?
    283     if (RenderPart* owner = frame->ownerRenderer()) {
    284         owner->setWidget(view);
    285         // Now the render part owns the view, so we don't any more.
    286     }
    287 
    288     view->updateCanHaveScrollbars();
    289 }
    290 
    291 @end
    292 
    293 @implementation WebFrameView
    294 
    295 - (id)initWithFrame:(NSRect)frame
    296 {
    297     self = [super initWithFrame:frame];
    298     if (!self)
    299         return nil;
    300 
    301     static bool didFirstTimeInitialization;
    302     if (!didFirstTimeInitialization) {
    303         didFirstTimeInitialization = true;
    304         InitWebCoreSystemInterface();
    305 
    306         // Need to tell WebCore what function to call for the "History Item has Changed" notification.
    307         // Note: We also do this in WebHistoryItem's init method.
    308         WebCore::notifyHistoryItemChanged = WKNotifyHistoryItemChanged;
    309 
    310         [WebViewFactory createSharedFactory];
    311 
    312 // FIXME: Remove the NSAppKitVersionNumberWithDeferredWindowDisplaySupport check once
    313 // once AppKit's Deferred Window Display support is available.
    314 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) || !defined(NSAppKitVersionNumberWithDeferredWindowDisplaySupport)
    315         // CoreGraphics deferred updates are disabled if WebKitEnableCoalescedUpdatesPreferenceKey is NO
    316         // or has no value. For compatibility with Mac OS X 10.5 and lower, deferred updates are off by default.
    317         if (![[NSUserDefaults standardUserDefaults] boolForKey:WebKitEnableDeferredUpdatesPreferenceKey])
    318             WKDisableCGDeferredUpdates();
    319 #endif
    320         if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_MAIN_THREAD_EXCEPTIONS))
    321             setDefaultThreadViolationBehavior(LogOnFirstThreadViolation, ThreadViolationRoundOne);
    322 
    323         bool throwExceptionsForRoundTwo = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_ROUND_TWO_MAIN_THREAD_EXCEPTIONS);
    324 #ifdef MAIL_THREAD_WORKAROUND
    325         // Even if old Mail is linked with new WebKit, don't throw exceptions.
    326         if ([WebResource _needMailThreadWorkaroundIfCalledOffMainThread])
    327             throwExceptionsForRoundTwo = false;
    328 #endif
    329         if (!throwExceptionsForRoundTwo)
    330             setDefaultThreadViolationBehavior(LogOnFirstThreadViolation, ThreadViolationRoundTwo);
    331     }
    332 
    333     _private = [[WebFrameViewPrivate alloc] init];
    334 
    335     WebDynamicScrollBarsView *scrollView = [[WebDynamicScrollBarsView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, frame.size.width, frame.size.height)];
    336     _private->frameScrollView = scrollView;
    337     [scrollView setContentView:[[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]];
    338     [scrollView setDrawsBackground:NO];
    339     [scrollView setHasVerticalScroller:NO];
    340     [scrollView setHasHorizontalScroller:NO];
    341     [scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    342     [scrollView setLineScroll:Scrollbar::pixelsPerLineStep()];
    343     [self addSubview:scrollView];
    344 
    345     // Don't call our overridden version of setNextKeyView here; we need to make the standard NSView
    346     // link between us and our subview so that previousKeyView and previousValidKeyView work as expected.
    347     // This works together with our becomeFirstResponder and setNextKeyView overrides.
    348     [super setNextKeyView:scrollView];
    349 
    350     return self;
    351 }
    352 
    353 - (void)dealloc
    354 {
    355     if (_private && _private->includedInWebKitStatistics)
    356         --WebFrameViewCount;
    357 
    358     [_private release];
    359     _private = nil;
    360 
    361     [super dealloc];
    362 }
    363 
    364 - (void)finalize
    365 {
    366     if (_private && _private->includedInWebKitStatistics)
    367         --WebFrameViewCount;
    368 
    369     [super finalize];
    370 }
    371 
    372 - (WebFrame *)webFrame
    373 {
    374     // This method can be called beneath -[NSView dealloc] after _private has been cleared.
    375     return _private ? _private->webFrame : nil;
    376 }
    377 
    378 - (void)setAllowsScrolling:(BOOL)flag
    379 {
    380     WebCore::Frame *frame = core([self webFrame]);
    381     if (WebCore::FrameView *view = frame? frame->view() : 0)
    382         view->setCanHaveScrollbars(flag);
    383 }
    384 
    385 - (BOOL)allowsScrolling
    386 {
    387     WebCore::Frame *frame = core([self webFrame]);
    388     if (WebCore::FrameView *view = frame? frame->view() : 0)
    389         return view->canHaveScrollbars();
    390     return YES;
    391 }
    392 
    393 - (NSView <WebDocumentView> *)documentView
    394 {
    395     return [[self _scrollView] documentView];
    396 }
    397 
    398 - (BOOL)acceptsFirstResponder
    399 {
    400     // We always accept first responder; this matches OS X 10.2 WebKit
    401     // behavior (see 3469791).
    402     return YES;
    403 }
    404 
    405 - (BOOL)becomeFirstResponder
    406 {
    407     // This works together with setNextKeyView to splice the WebFrameView into
    408     // the key loop similar to the way NSScrollView does this. Note that
    409     // WebView has similar code.
    410 
    411     NSWindow *window = [self window];
    412     if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
    413         NSView *previousValidKeyView = [self previousValidKeyView];
    414         // If we couldn't find a previous valid key view, ask the WebView. This handles frameset
    415         // cases (one is mentioned in Radar bug 3748628). Note that previousValidKeyView should
    416         // never be self but can be due to AppKit oddness (mentioned in Radar bug 3748628).
    417         if (previousValidKeyView == nil || previousValidKeyView == self)
    418             previousValidKeyView = [[[self webFrame] webView] previousValidKeyView];
    419         [window makeFirstResponder:previousValidKeyView];
    420     } else {
    421         // If the scroll view won't accept first-responderness now, then just become
    422         // the first responder ourself like a normal view. This lets us be the first
    423         // responder in cases where no page has yet been loaded.
    424         if ([[self _scrollView] acceptsFirstResponder])
    425             [window makeFirstResponder:[self _scrollView]];
    426     }
    427 
    428     return YES;
    429 }
    430 
    431 - (void)setNextKeyView:(NSView *)aView
    432 {
    433     // This works together with becomeFirstResponder to splice the WebFrameView into
    434     // the key loop similar to the way NSScrollView does this. Note that
    435     // WebView has very similar code.
    436     if ([self _scrollView] != nil) {
    437         [[self _scrollView] setNextKeyView:aView];
    438     } else {
    439         [super setNextKeyView:aView];
    440     }
    441 }
    442 
    443 - (BOOL)isOpaque
    444 {
    445     return [[self _webView] drawsBackground];
    446 }
    447 
    448 - (void)drawRect:(NSRect)rect
    449 {
    450     if ([self documentView] == nil) {
    451         // Need to paint ourselves if there's no documentView to do it instead.
    452         if ([[self _webView] drawsBackground]) {
    453             [[[self _webView] backgroundColor] set];
    454             NSRectFill(rect);
    455         }
    456     } else {
    457 #ifndef NDEBUG
    458         if ([[self _scrollView] drawsBackground]) {
    459             [[NSColor cyanColor] set];
    460             NSRectFill(rect);
    461         }
    462 #endif
    463     }
    464 }
    465 
    466 - (NSRect)visibleRect
    467 {
    468     // This method can be called beneath -[NSView dealloc] after we have cleared _private.
    469     if (!_private)
    470         return [super visibleRect];
    471 
    472     // FIXME: <rdar://problem/6213380> This method does not work correctly with transforms, for two reasons:
    473     // 1) [super visibleRect] does not account for the transform, since it is not represented
    474     //    in the NSView hierarchy.
    475     // 2) -_getVisibleRect: does not correct for transforms.
    476 
    477     NSRect rendererVisibleRect;
    478     if (![[self webFrame] _getVisibleRect:&rendererVisibleRect])
    479         return [super visibleRect];
    480 
    481     if (NSIsEmptyRect(rendererVisibleRect))
    482         return NSZeroRect;
    483 
    484     NSRect viewVisibleRect = [super visibleRect];
    485     if (NSIsEmptyRect(viewVisibleRect))
    486         return NSZeroRect;
    487 
    488     NSRect frame = [self frame];
    489     // rendererVisibleRect is in the parent's coordinate space, and frame is in the superview's coordinate space.
    490     // The return value from this method needs to be in this view's coordinate space. We get that right by subtracting
    491     // the origins (and correcting for flipping), but when we support transforms, we will need to do better than this.
    492     rendererVisibleRect.origin.x -= frame.origin.x;
    493     rendererVisibleRect.origin.y = NSMaxY(frame) - NSMaxY(rendererVisibleRect);
    494     return NSIntersectionRect(rendererVisibleRect, viewVisibleRect);
    495 }
    496 
    497 - (void)setFrameSize:(NSSize)size
    498 {
    499     if (!NSEqualSizes(size, [self frame].size)) {
    500         // See WebFrameLoaderClient::provisionalLoadStarted.
    501         if ([[[self webFrame] webView] drawsBackground])
    502             [[self _scrollView] setDrawsBackground:YES];
    503         if (Frame* coreFrame = [self _web_frame]) {
    504             if (FrameView* coreFrameView = coreFrame->view())
    505                 coreFrameView->setNeedsLayout();
    506         }
    507     }
    508     [super setFrameSize:size];
    509 }
    510 
    511 - (void)setBoundsSize:(NSSize)size
    512 {
    513     [super setBoundsSize:size];
    514     [[self _scrollView] setFrameSize:size];
    515 }
    516 
    517 - (void)viewDidMoveToWindow
    518 {
    519     // See WebFrameLoaderClient::provisionalLoadStarted.
    520     // Need to check _private for nil because this can be called inside -[WebView initWithCoder:].
    521     if (_private && [[[self webFrame] webView] drawsBackground])
    522         [[self _scrollView] setDrawsBackground:YES];
    523     [super viewDidMoveToWindow];
    524 }
    525 
    526 - (BOOL)_scrollOverflowInDirection:(ScrollDirection)direction granularity:(ScrollGranularity)granularity
    527 {
    528     // scrolling overflows is only applicable if we're dealing with an WebHTMLView
    529     if (![[self documentView] isKindOfClass:[WebHTMLView class]])
    530         return NO;
    531     Frame* frame = core([self webFrame]);
    532     if (!frame)
    533         return NO;
    534     return frame->eventHandler()->scrollOverflow(direction, granularity);
    535 }
    536 
    537 
    538 - (BOOL)_isVerticalDocument
    539 {
    540     Frame* coreFrame = [self _web_frame];
    541     if (!coreFrame)
    542         return YES;
    543     Document* document = coreFrame->document();
    544     if (!document)
    545         return YES;
    546     RenderObject* renderView = document->renderer();
    547     if (!renderView)
    548         return YES;
    549     return renderView->style()->isHorizontalWritingMode();
    550 }
    551 
    552 - (BOOL)_isFlippedDocument
    553 {
    554     Frame* coreFrame = [self _web_frame];
    555     if (!coreFrame)
    556         return NO;
    557     Document* document = coreFrame->document();
    558     if (!document)
    559         return NO;
    560     RenderObject* renderView = document->renderer();
    561     if (!renderView)
    562         return NO;
    563     return renderView->style()->isFlippedBlocksWritingMode();
    564 }
    565 
    566 - (BOOL)_scrollToBeginningOfDocument
    567 {
    568     if ([self _scrollOverflowInDirection:ScrollUp granularity:ScrollByDocument])
    569         return YES;
    570     if (![self _isScrollable])
    571         return NO;
    572     NSPoint point = [[[self _scrollView] documentView] frame].origin;
    573     point.x += [[self _scrollView] scrollOrigin].x;
    574     point.y += [[self _scrollView] scrollOrigin].y;
    575     return [[self _contentView] _scrollTo:&point animate:YES];
    576 }
    577 
    578 - (BOOL)_scrollToEndOfDocument
    579 {
    580     if ([self _scrollOverflowInDirection:ScrollDown granularity:ScrollByDocument])
    581         return YES;
    582     if (![self _isScrollable])
    583         return NO;
    584     NSRect frame = [[[self _scrollView] documentView] frame];
    585 
    586     bool isVertical = [self _isVerticalDocument];
    587     bool isFlipped = [self _isFlippedDocument];
    588 
    589     NSPoint point;
    590     if (isVertical) {
    591         if (!isFlipped)
    592             point = NSMakePoint(frame.origin.x, NSMaxY(frame));
    593         else
    594             point = NSMakePoint(frame.origin.x, NSMinY(frame));
    595     } else {
    596         if (!isFlipped)
    597             point = NSMakePoint(NSMaxX(frame), frame.origin.y);
    598         else
    599             point = NSMakePoint(NSMinX(frame), frame.origin.y);
    600     }
    601 
    602     // Reset the position opposite to the block progression direction.
    603     if (isVertical)
    604         point.x += [[self _scrollView] scrollOrigin].x;
    605     else
    606         point.y += [[self _scrollView] scrollOrigin].y;
    607     return [[self _contentView] _scrollTo:&point animate:YES];
    608 }
    609 
    610 - (void)scrollToBeginningOfDocument:(id)sender
    611 {
    612     if ([self _scrollToBeginningOfDocument])
    613         return;
    614 
    615     if (WebFrameView *child = [self _largestScrollableChild]) {
    616         if ([child _scrollToBeginningOfDocument])
    617             return;
    618     }
    619     [[self nextResponder] tryToPerform:@selector(scrollToBeginningOfDocument:) with:sender];
    620 }
    621 
    622 - (void)scrollToEndOfDocument:(id)sender
    623 {
    624     if ([self _scrollToEndOfDocument])
    625         return;
    626 
    627     if (WebFrameView *child = [self _largestScrollableChild]) {
    628         if ([child _scrollToEndOfDocument])
    629             return;
    630     }
    631     [[self nextResponder] tryToPerform:@selector(scrollToEndOfDocument:) with:sender];
    632 }
    633 
    634 - (void)_goBack
    635 {
    636     [[self _webView] goBack];
    637 }
    638 
    639 - (void)_goForward
    640 {
    641     [[self _webView] goForward];
    642 }
    643 
    644 - (BOOL)_scrollVerticallyBy:(float)delta
    645 {
    646     // This method uses the secret method _scrollTo on NSClipView.
    647     // It does that because it needs to know definitively whether scrolling was
    648     // done or not to help implement the "scroll parent if we are at the limit" feature.
    649     // In the presence of smooth scrolling, there's no easy way to tell if the method
    650     // did any scrolling or not with the public API.
    651     NSPoint point = [[self _contentView] bounds].origin;
    652     point.y += delta;
    653     return [[self _contentView] _scrollTo:&point animate:YES];
    654 }
    655 
    656 - (BOOL)_scrollHorizontallyBy:(float)delta
    657 {
    658     NSPoint point = [[self _contentView] bounds].origin;
    659     point.x += delta;
    660     return [[self _contentView] _scrollTo:&point animate:YES];
    661 }
    662 
    663 - (float)_horizontalKeyboardScrollDistance
    664 {
    665     // Arrow keys scroll the same distance that clicking the scroll arrow does.
    666     return [[self _scrollView] horizontalLineScroll];
    667 }
    668 
    669 - (float)_horizontalPageScrollDistance
    670 {
    671     float width = [[self _contentView] bounds].size.width;
    672     return max<float>(width * Scrollbar::minFractionToStepWhenPaging(), width - Scrollbar::maxOverlapBetweenPages());
    673 }
    674 
    675 - (BOOL)_pageVertically:(BOOL)up
    676 {
    677     if ([self _scrollOverflowInDirection:up ? ScrollUp : ScrollDown granularity:ScrollByPage])
    678         return YES;
    679 
    680     if (![self _isScrollable])
    681         return [[self _largestScrollableChild] _pageVertically:up];
    682 
    683     float delta = [self _verticalPageScrollDistance];
    684     return [self _scrollVerticallyBy:up ? -delta : delta];
    685 }
    686 
    687 - (BOOL)_pageHorizontally:(BOOL)left
    688 {
    689     if ([self _scrollOverflowInDirection:left ? ScrollLeft : ScrollRight granularity:ScrollByPage])
    690         return YES;
    691 
    692     if (![self _isScrollable])
    693         return [[self _largestScrollableChild] _pageHorizontally:left];
    694 
    695     float delta = [self _horizontalPageScrollDistance];
    696     return [self _scrollHorizontallyBy:left ? -delta : delta];
    697 }
    698 
    699 - (BOOL)_pageInBlockProgressionDirection:(BOOL)forward
    700 {
    701     // Determine whether we're calling _pageVertically or _pageHorizontally.
    702     BOOL isVerticalDocument = [self _isVerticalDocument];
    703     BOOL isFlippedBlock = [self _isFlippedDocument];
    704     if (isVerticalDocument)
    705         return [self _pageVertically:isFlippedBlock ? !forward : forward];
    706     return [self _pageHorizontally:isFlippedBlock ? !forward : forward];
    707 }
    708 
    709 - (BOOL)_scrollLineVertically:(BOOL)up
    710 {
    711     if ([self _scrollOverflowInDirection:up ? ScrollUp : ScrollDown granularity:ScrollByLine])
    712         return YES;
    713 
    714     if (![self _isScrollable])
    715         return [[self _largestScrollableChild] _scrollLineVertically:up];
    716 
    717     float delta = [self _verticalKeyboardScrollDistance];
    718     return [self _scrollVerticallyBy:up ? -delta : delta];
    719 }
    720 
    721 - (BOOL)_scrollLineHorizontally:(BOOL)left
    722 {
    723     if ([self _scrollOverflowInDirection:left ? ScrollLeft : ScrollRight granularity:ScrollByLine])
    724         return YES;
    725 
    726     if (![self _isScrollable])
    727         return [[self _largestScrollableChild] _scrollLineHorizontally:left];
    728 
    729     float delta = [self _horizontalKeyboardScrollDistance];
    730     return [self _scrollHorizontallyBy:left ? -delta : delta];
    731 }
    732 
    733 - (void)scrollPageUp:(id)sender
    734 {
    735     if (![self _pageInBlockProgressionDirection:YES]) {
    736         // If we were already at the top, tell the next responder to scroll if it can.
    737         [[self nextResponder] tryToPerform:@selector(scrollPageUp:) with:sender];
    738     }
    739 }
    740 
    741 - (void)scrollPageDown:(id)sender
    742 {
    743     if (![self _pageInBlockProgressionDirection:NO]) {
    744         // If we were already at the bottom, tell the next responder to scroll if it can.
    745         [[self nextResponder] tryToPerform:@selector(scrollPageDown:) with:sender];
    746     }
    747 }
    748 
    749 - (void)scrollLineUp:(id)sender
    750 {
    751     if (![self _scrollLineVertically:YES])
    752         [[self nextResponder] tryToPerform:@selector(scrollLineUp:) with:sender];
    753 }
    754 
    755 - (void)scrollLineDown:(id)sender
    756 {
    757     if (![self _scrollLineVertically:NO])
    758         [[self nextResponder] tryToPerform:@selector(scrollLineDown:) with:sender];
    759 }
    760 
    761 - (BOOL)_firstResponderIsFormControl
    762 {
    763     NSResponder *firstResponder = [[self window] firstResponder];
    764 
    765     // WebHTMLView is an NSControl subclass these days, but it's not a form control
    766     if ([firstResponder isKindOfClass:[WebHTMLView class]]) {
    767         return NO;
    768     }
    769     return [firstResponder isKindOfClass:[NSControl class]];
    770 }
    771 
    772 - (void)keyDown:(NSEvent *)event
    773 {
    774     // Implement common browser behaviors for all kinds of content.
    775 
    776     // FIXME: This is not a good time to execute commands for WebHTMLView. We should run these at the time commands sent by key bindings
    777     // are executed for consistency.
    778     // This doesn't work automatically because most of the keys handled here are translated into moveXXX commands, which are not handled
    779     // by Editor when focus is not in editable content.
    780 
    781     NSString *characters = [event characters];
    782     int index, count;
    783     BOOL callSuper = YES;
    784     Frame* coreFrame = [self _web_frame];
    785     BOOL maintainsBackForwardList = coreFrame && static_cast<BackForwardListImpl*>(coreFrame->page()->backForwardList())->enabled() ? YES : NO;
    786 
    787     count = [characters length];
    788     for (index = 0; index < count; ++index) {
    789         switch ([characters characterAtIndex:index]) {
    790             case NSDeleteCharacter:
    791                 if (!maintainsBackForwardList) {
    792                     callSuper = YES;
    793                     break;
    794                 }
    795                 // This odd behavior matches some existing browsers,
    796                 // including Windows IE
    797                 if ([event modifierFlags] & NSShiftKeyMask) {
    798                     [self _goForward];
    799                 } else {
    800                     [self _goBack];
    801                 }
    802                 callSuper = NO;
    803                 break;
    804             case SpaceKey:
    805                 // Checking for a control will allow events to percolate
    806                 // correctly when the focus is on a form control and we
    807                 // are in full keyboard access mode.
    808                 if ((![self allowsScrolling] && ![self _largestScrollableChild]) || [self _firstResponderIsFormControl]) {
    809                     callSuper = YES;
    810                     break;
    811                 }
    812                 if ([event modifierFlags] & NSShiftKeyMask) {
    813                     [self scrollPageUp:nil];
    814                 } else {
    815                     [self scrollPageDown:nil];
    816                 }
    817                 callSuper = NO;
    818                 break;
    819             case NSPageUpFunctionKey:
    820                 if (![self allowsScrolling] && ![self _largestScrollableChild]) {
    821                     callSuper = YES;
    822                     break;
    823                 }
    824                 [self scrollPageUp:nil];
    825                 callSuper = NO;
    826                 break;
    827             case NSPageDownFunctionKey:
    828                 if (![self allowsScrolling] && ![self _largestScrollableChild]) {
    829                     callSuper = YES;
    830                     break;
    831                 }
    832                 [self scrollPageDown:nil];
    833                 callSuper = NO;
    834                 break;
    835             case NSHomeFunctionKey:
    836                 if (![self allowsScrolling] && ![self _largestScrollableChild]) {
    837                     callSuper = YES;
    838                     break;
    839                 }
    840                 [self scrollToBeginningOfDocument:nil];
    841                 callSuper = NO;
    842                 break;
    843             case NSEndFunctionKey:
    844                 if (![self allowsScrolling] && ![self _largestScrollableChild]) {
    845                     callSuper = YES;
    846                     break;
    847                 }
    848                 [self scrollToEndOfDocument:nil];
    849                 callSuper = NO;
    850                 break;
    851             case NSUpArrowFunctionKey:
    852                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
    853                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
    854                     callSuper = YES;
    855                     break;
    856                 }
    857                 if ((![self allowsScrolling] && ![self _largestScrollableChild]) ||
    858                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
    859                     // Let arrow keys go through to pop up buttons
    860                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a
    861                     // pop-up menu should pop the menu
    862                     callSuper = YES;
    863                     break;
    864                 }
    865                 if ([event modifierFlags] & NSCommandKeyMask) {
    866                     [self scrollToBeginningOfDocument:nil];
    867                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
    868                     [self scrollPageUp:nil];
    869                 } else {
    870                     [self scrollLineUp:nil];
    871                 }
    872                 callSuper = NO;
    873                 break;
    874             case NSDownArrowFunctionKey:
    875                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
    876                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
    877                     callSuper = YES;
    878                     break;
    879                 }
    880                 if ((![self allowsScrolling] && ![self _largestScrollableChild]) ||
    881                     [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) {
    882                     // Let arrow keys go through to pop up buttons
    883                     // <rdar://problem/3455910>: hitting up or down arrows when focus is on a
    884                     // pop-up menu should pop the menu
    885                     callSuper = YES;
    886                     break;
    887                 }
    888                 if ([event modifierFlags] & NSCommandKeyMask) {
    889                     [self scrollToEndOfDocument:nil];
    890                 } else if ([event modifierFlags] & NSAlternateKeyMask) {
    891                     [self scrollPageDown:nil];
    892                 } else {
    893                     [self scrollLineDown:nil];
    894                 }
    895                 callSuper = NO;
    896                 break;
    897             case NSLeftArrowFunctionKey:
    898                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
    899                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
    900                     callSuper = YES;
    901                     break;
    902                 }
    903                 // Check back/forward related keys.
    904                 if ([event modifierFlags] & NSCommandKeyMask) {
    905                     if (!maintainsBackForwardList) {
    906                         callSuper = YES;
    907                         break;
    908                     }
    909                     [self _goBack];
    910                 } else {
    911                     // Now check scrolling related keys.
    912                     if ((![self allowsScrolling] && ![self _largestScrollableChild])) {
    913                         callSuper = YES;
    914                         break;
    915                     }
    916 
    917                     if ([event modifierFlags] & NSAlternateKeyMask) {
    918                         [self _pageHorizontally:YES];
    919                     } else {
    920                         [self _scrollLineHorizontally:YES];
    921                     }
    922                 }
    923                 callSuper = NO;
    924                 break;
    925             case NSRightArrowFunctionKey:
    926                 // We don't handle shifted or control-arrow keys here, so let super have a chance.
    927                 if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) {
    928                     callSuper = YES;
    929                     break;
    930                 }
    931                 // Check back/forward related keys.
    932                 if ([event modifierFlags] & NSCommandKeyMask) {
    933                     if (!maintainsBackForwardList) {
    934                         callSuper = YES;
    935                         break;
    936                     }
    937                     [self _goForward];
    938                 } else {
    939                     // Now check scrolling related keys.
    940                     if ((![self allowsScrolling] && ![self _largestScrollableChild])) {
    941                         callSuper = YES;
    942                         break;
    943                     }
    944 
    945                     if ([event modifierFlags] & NSAlternateKeyMask) {
    946                         [self _pageHorizontally:NO];
    947                     } else {
    948                         [self _scrollLineHorizontally:NO];
    949                     }
    950                 }
    951                 callSuper = NO;
    952                 break;
    953         }
    954     }
    955 
    956     if (callSuper) {
    957         [super keyDown:event];
    958     } else {
    959         // if we did something useful, get the cursor out of the way
    960         [NSCursor setHiddenUntilMouseMoves:YES];
    961     }
    962 }
    963 
    964 - (NSView *)_webcore_effectiveFirstResponder
    965 {
    966     NSView *view = [self documentView];
    967     return view ? [view _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
    968 }
    969 
    970 - (BOOL)canPrintHeadersAndFooters
    971 {
    972     NSView *documentView = [[self _scrollView] documentView];
    973     if ([documentView respondsToSelector:@selector(canPrintHeadersAndFooters)]) {
    974         return [(id)documentView canPrintHeadersAndFooters];
    975     }
    976     return NO;
    977 }
    978 
    979 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
    980 {
    981     NSView *documentView = [[self _scrollView] documentView];
    982     if (!documentView) {
    983         return nil;
    984     }
    985     if ([documentView respondsToSelector:@selector(printOperationWithPrintInfo:)]) {
    986         return [(id)documentView printOperationWithPrintInfo:printInfo];
    987     }
    988     return [NSPrintOperation printOperationWithView:documentView printInfo:printInfo];
    989 }
    990 
    991 - (BOOL)documentViewShouldHandlePrint
    992 {
    993     NSView *documentView = [[self _scrollView] documentView];
    994     if (documentView && [documentView respondsToSelector:@selector(documentViewShouldHandlePrint)])
    995         return [(id)documentView documentViewShouldHandlePrint];
    996 
    997     return NO;
    998 }
    999 
   1000 - (void)printDocumentView
   1001 {
   1002     NSView *documentView = [[self _scrollView] documentView];
   1003     if (documentView && [documentView respondsToSelector:@selector(printDocumentView)])
   1004         [(id)documentView printDocumentView];
   1005 }
   1006 
   1007 @end
   1008 
   1009 @implementation WebFrameView (WebPrivate)
   1010 
   1011 - (float)_area
   1012 {
   1013     NSRect frame = [self frame];
   1014     return frame.size.height * frame.size.width;
   1015 }
   1016 
   1017 - (BOOL)_isScrollable
   1018 {
   1019     WebDynamicScrollBarsView *scrollView = [self _scrollView];
   1020     return [scrollView horizontalScrollingAllowed] || [scrollView verticalScrollingAllowed];
   1021 }
   1022 
   1023 - (WebFrameView *)_largestScrollableChild
   1024 {
   1025     WebFrameView *largest = nil;
   1026     NSArray *frameChildren = [[self webFrame] childFrames];
   1027 
   1028     unsigned i;
   1029     for (i=0; i < [frameChildren count]; i++) {
   1030         WebFrameView *childFrameView = [[frameChildren objectAtIndex:i] frameView];
   1031         WebFrameView *scrollableFrameView = [childFrameView _isScrollable] ? childFrameView : [childFrameView _largestScrollableChild];
   1032         if (!scrollableFrameView)
   1033             continue;
   1034 
   1035         // Some ads lurk in child frames of zero width and height, see radar 4406994. These don't count as scrollable.
   1036         // Maybe someday we'll discover that this minimum area check should be larger, but this covers the known cases.
   1037         float area = [scrollableFrameView _area];
   1038         if (area < 1.0)
   1039             continue;
   1040 
   1041         if (!largest || (area > [largest _area])) {
   1042             largest = scrollableFrameView;
   1043         }
   1044     }
   1045 
   1046     return largest;
   1047 }
   1048 
   1049 - (BOOL)_hasScrollBars
   1050 {
   1051     // FIXME: This method was used by Safari 4.0.x and older versions, but has not been used by any other WebKit
   1052     // clients to my knowledge, and will not be used by future versions of Safari. It can probably be removed
   1053     // once we no longer need to keep nightly WebKit builds working with Safari 4.0.x and earlier.
   1054     NSScrollView *scrollView = [self _scrollView];
   1055     return [scrollView hasHorizontalScroller] || [scrollView hasVerticalScroller];
   1056 }
   1057 
   1058 - (WebFrameView *)_largestChildWithScrollBars
   1059 {
   1060     // FIXME: This method was used by Safari 4.0.x and older versions, but has not been used by any other WebKit
   1061     // clients to my knowledge, and will not be used by future versions of Safari. It can probably be removed
   1062     // once we no longer need to keep nightly WebKit builds working with Safari 4.0.x and earlier.
   1063     WebFrameView *largest = nil;
   1064     NSArray *frameChildren = [[self webFrame] childFrames];
   1065 
   1066     unsigned i;
   1067     for (i=0; i < [frameChildren count]; i++) {
   1068         WebFrameView *childFrameView = [[frameChildren objectAtIndex:i] frameView];
   1069         WebFrameView *scrollableFrameView = [childFrameView _hasScrollBars] ? childFrameView : [childFrameView _largestChildWithScrollBars];
   1070         if (!scrollableFrameView)
   1071             continue;
   1072 
   1073         // Some ads lurk in child frames of zero width and height, see radar 4406994. These don't count as scrollable.
   1074         // Maybe someday we'll discover that this minimum area check should be larger, but this covers the known cases.
   1075         float area = [scrollableFrameView _area];
   1076         if (area < 1.0)
   1077             continue;
   1078 
   1079         if (!largest || (area > [largest _area])) {
   1080             largest = scrollableFrameView;
   1081         }
   1082     }
   1083 
   1084     return largest;
   1085 }
   1086 
   1087 - (NSClipView *)_contentView
   1088 {
   1089     return [[self _scrollView] contentView];
   1090 }
   1091 
   1092 - (Class)_customScrollViewClass
   1093 {
   1094     if ([_private->frameScrollView class] == [WebDynamicScrollBarsView class])
   1095         return nil;
   1096     return [_private->frameScrollView class];
   1097 }
   1098 
   1099 - (void)_setCustomScrollViewClass:(Class)customClass
   1100 {
   1101     if (!customClass)
   1102         customClass = [WebDynamicScrollBarsView class];
   1103     ASSERT([customClass isSubclassOfClass:[WebDynamicScrollBarsView class]]);
   1104     if (customClass == [_private->frameScrollView class])
   1105         return;
   1106     if (![customClass isSubclassOfClass:[WebDynamicScrollBarsView class]])
   1107         return;
   1108 
   1109     WebDynamicScrollBarsView *oldScrollView = _private->frameScrollView; // already retained
   1110     NSView <WebDocumentView> *documentView = [[self documentView] retain];
   1111 
   1112     WebDynamicScrollBarsView *scrollView  = [[customClass alloc] initWithFrame:[oldScrollView frame]];
   1113     [scrollView setContentView:[[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]];
   1114     [scrollView setDrawsBackground:[oldScrollView drawsBackground]];
   1115     [scrollView setHasVerticalScroller:[oldScrollView hasVerticalScroller]];
   1116     [scrollView setHasHorizontalScroller:[oldScrollView hasHorizontalScroller]];
   1117     [scrollView setAutoresizingMask:[oldScrollView autoresizingMask]];
   1118     [scrollView setLineScroll:[oldScrollView lineScroll]];
   1119     [self addSubview:scrollView];
   1120 
   1121     // don't call our overridden version here; we need to make the standard NSView link between us
   1122     // and our subview so that previousKeyView and previousValidKeyView work as expected. This works
   1123     // together with our becomeFirstResponder and setNextKeyView overrides.
   1124     [super setNextKeyView:scrollView];
   1125 
   1126     _private->frameScrollView = scrollView;
   1127 
   1128     [self _setDocumentView:documentView];
   1129     [self _install];
   1130 
   1131     [oldScrollView removeFromSuperview];
   1132     [oldScrollView release];
   1133     [documentView release];
   1134 }
   1135 
   1136 @end
   1137