Home | History | Annotate | Download | only in WebView
      1 /*
      2  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
      3  *           (C) 2006, 2007 Graham Dennis (graham.dennis (at) gmail.com)
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1.  Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  * 2.  Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     15  *     its contributors may be used to endorse or promote products derived
     16  *     from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #import "WebHTMLView.h"
     31 
     32 #import "DOMCSSStyleDeclarationInternal.h"
     33 #import "DOMDocumentFragmentInternal.h"
     34 #import "DOMDocumentInternal.h"
     35 #import "DOMNodeInternal.h"
     36 #import "DOMRangeInternal.h"
     37 #import "WebArchive.h"
     38 #import "WebClipView.h"
     39 #import "WebDOMOperationsInternal.h"
     40 #import "WebDataSourceInternal.h"
     41 #import "WebDefaultUIDelegate.h"
     42 #import "WebDelegateImplementationCaching.h"
     43 #import "WebDocumentInternal.h"
     44 #import "WebDynamicScrollBarsViewInternal.h"
     45 #import "WebEditingDelegate.h"
     46 #import "WebElementDictionary.h"
     47 #import "WebFrameInternal.h"
     48 #import "WebFramePrivate.h"
     49 #import "WebFrameViewInternal.h"
     50 #import "WebHTMLRepresentationPrivate.h"
     51 #import "WebHTMLViewInternal.h"
     52 #import "WebKitLogging.h"
     53 #import "WebKitNSStringExtras.h"
     54 #import "WebKitVersionChecks.h"
     55 #import "WebLocalizableStringsInternal.h"
     56 #import "WebNSEventExtras.h"
     57 #import "WebNSFileManagerExtras.h"
     58 #import "WebNSImageExtras.h"
     59 #import "WebNSObjectExtras.h"
     60 #import "WebNSPasteboardExtras.h"
     61 #import "WebNSPrintOperationExtras.h"
     62 #import "WebNSURLExtras.h"
     63 #import "WebNSViewExtras.h"
     64 #import "WebNetscapePluginView.h"
     65 #import "WebNodeHighlight.h"
     66 #import "WebPluginController.h"
     67 #import "WebPreferences.h"
     68 #import "WebPreferencesPrivate.h"
     69 #import "WebResourcePrivate.h"
     70 #import "WebTextCompletionController.h"
     71 #import "WebTypesInternal.h"
     72 #import "WebUIDelegatePrivate.h"
     73 #import "WebViewInternal.h"
     74 #import <AppKit/NSAccessibility.h>
     75 #import <ApplicationServices/ApplicationServices.h>
     76 #import <WebCore/CSSMutableStyleDeclaration.h>
     77 #import <WebCore/CachedImage.h>
     78 #import <WebCore/CachedResourceClient.h>
     79 #import <WebCore/ColorMac.h>
     80 #import <WebCore/ContextMenu.h>
     81 #import <WebCore/ContextMenuController.h>
     82 #import <WebCore/Document.h>
     83 #import <WebCore/DocumentFragment.h>
     84 #import <WebCore/DocumentMarkerController.h>
     85 #import <WebCore/DragController.h>
     86 #import <WebCore/Editor.h>
     87 #import <WebCore/EditorDeleteAction.h>
     88 #import <WebCore/Element.h>
     89 #import <WebCore/EventHandler.h>
     90 #import <WebCore/ExceptionHandlers.h>
     91 #import <WebCore/FloatRect.h>
     92 #import <WebCore/FocusController.h>
     93 #import <WebCore/Frame.h>
     94 #import <WebCore/FrameLoader.h>
     95 #import <WebCore/FrameView.h>
     96 #import <WebCore/HTMLConverter.h>
     97 #import <WebCore/HTMLNames.h>
     98 #import <WebCore/HitTestResult.h>
     99 #import <WebCore/Image.h>
    100 #import <WebCore/KeyboardEvent.h>
    101 #import <WebCore/LegacyWebArchive.h>
    102 #import <WebCore/MIMETypeRegistry.h>
    103 #import <WebCore/Page.h>
    104 #import <WebCore/PlatformKeyboardEvent.h>
    105 #import <WebCore/Range.h>
    106 #import <WebCore/RenderWidget.h>
    107 #import <WebCore/RenderView.h>
    108 #import <WebCore/RuntimeApplicationChecks.h>
    109 #import <WebCore/SelectionController.h>
    110 #import <WebCore/SharedBuffer.h>
    111 #import <WebCore/SimpleFontData.h>
    112 #import <WebCore/Text.h>
    113 #import <WebCore/WebCoreObjCExtras.h>
    114 #import <WebCore/WebFontCache.h>
    115 #import <WebCore/WebNSAttributedStringExtras.h>
    116 #import <WebCore/markup.h>
    117 #import <WebKit/DOM.h>
    118 #import <WebKit/DOMExtensions.h>
    119 #import <WebKit/DOMPrivate.h>
    120 #import <WebKitSystemInterface.h>
    121 #import <dlfcn.h>
    122 #import <limits>
    123 #import <runtime/InitializeThreading.h>
    124 #import <wtf/Threading.h>
    125 
    126 #if USE(ACCELERATED_COMPOSITING)
    127 #import <QuartzCore/QuartzCore.h>
    128 #endif
    129 
    130 using namespace WebCore;
    131 using namespace HTMLNames;
    132 using namespace WTF;
    133 using namespace std;
    134 
    135 @interface WebMenuTarget : NSObject {
    136     WebCore::ContextMenuController* _menuController;
    137 }
    138 + (WebMenuTarget*)sharedMenuTarget;
    139 - (WebCore::ContextMenuController*)menuController;
    140 - (void)setMenuController:(WebCore::ContextMenuController*)menuController;
    141 - (void)forwardContextMenuAction:(id)sender;
    142 - (BOOL)validateMenuItem:(NSMenuItem *)item;
    143 @end
    144 
    145 static WebMenuTarget* target;
    146 
    147 @implementation WebMenuTarget
    148 
    149 + (WebMenuTarget*)sharedMenuTarget
    150 {
    151     if (!target)
    152         target = [[WebMenuTarget alloc] init];
    153     return target;
    154 }
    155 
    156 - (WebCore::ContextMenuController*)menuController
    157 {
    158     return _menuController;
    159 }
    160 
    161 - (void)setMenuController:(WebCore::ContextMenuController*)menuController
    162 {
    163     _menuController = menuController;
    164 }
    165 
    166 - (void)forwardContextMenuAction:(id)sender
    167 {
    168     WebCore::ContextMenuItem item(WebCore::ActionType, static_cast<WebCore::ContextMenuAction>([sender tag]), [sender title]);
    169     _menuController->contextMenuItemSelected(&item);
    170 }
    171 
    172 - (BOOL)validateMenuItem:(NSMenuItem *)item
    173 {
    174     WebCore::ContextMenuItem coreItem(item);
    175     ASSERT(_menuController->contextMenu());
    176     _menuController->checkOrEnableIfNeeded(coreItem);
    177     return coreItem.enabled();
    178 }
    179 
    180 @end
    181 
    182 @interface NSWindow (BorderViewAccess)
    183 - (NSView*)_web_borderView;
    184 @end
    185 
    186 @implementation NSWindow (BorderViewAccess)
    187 - (NSView*)_web_borderView
    188 {
    189     return _borderView;
    190 }
    191 @end
    192 
    193 @interface WebResponderChainSink : NSResponder {
    194     NSResponder* _lastResponderInChain;
    195     BOOL _receivedUnhandledCommand;
    196 }
    197 - (id)initWithResponderChain:(NSResponder *)chain;
    198 - (void)detach;
    199 - (BOOL)receivedUnhandledCommand;
    200 @end
    201 
    202 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
    203 static BOOL forceNSViewHitTest;
    204 
    205 // if YES, do the "top WebHTMLView" hit test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721])
    206 static BOOL forceWebHTMLViewHitTest;
    207 
    208 static WebHTMLView *lastHitView;
    209 
    210 static bool needsCursorRectsSupportAtPoint(NSWindow* window, NSPoint point)
    211 {
    212     forceNSViewHitTest = YES;
    213     NSView* view = [[window _web_borderView] hitTest:point];
    214     forceNSViewHitTest = NO;
    215 
    216     // WebHTMLView doesn't use cursor rects.
    217     if ([view isKindOfClass:[WebHTMLView class]])
    218         return false;
    219 
    220 #if ENABLE(NETSCAPE_PLUGIN_API)
    221     // Neither do NPAPI plug-ins.
    222     if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
    223         return false;
    224 #endif
    225 
    226     // Non-Web content, WebPDFView, and WebKit plug-ins use normal cursor handling.
    227     return true;
    228 }
    229 
    230 #ifndef BUILDING_ON_TIGER
    231 
    232 static IMP oldSetCursorForMouseLocationIMP;
    233 
    234 // Overriding an internal method is a hack; <rdar://problem/7662987> tracks finding a better solution.
    235 static void setCursor(NSWindow *self, SEL cmd, NSPoint point)
    236 {
    237     if (needsCursorRectsSupportAtPoint(self, point))
    238         oldSetCursorForMouseLocationIMP(self, cmd, point);
    239 }
    240 
    241 #else
    242 
    243 static IMP oldResetCursorRectsIMP;
    244 static IMP oldSetCursorIMP;
    245 static BOOL canSetCursor = YES;
    246 
    247 static void resetCursorRects(NSWindow* self, SEL cmd)
    248 {
    249     canSetCursor = needsCursorRectsSupportAtPoint(self, [self mouseLocationOutsideOfEventStream]);
    250     oldResetCursorRectsIMP(self, cmd);
    251     canSetCursor = YES;
    252 }
    253 
    254 static void setCursor(NSCursor* self, SEL cmd)
    255 {
    256     if (canSetCursor)
    257         oldSetCursorIMP(self, cmd);
    258 }
    259 
    260 #endif
    261 
    262 extern "C" {
    263 
    264 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
    265 
    266 extern NSString *NSMarkedClauseSegmentAttributeName;
    267 extern NSString *NSTextInputReplacementRangeAttributeName;
    268 
    269 }
    270 
    271 @interface NSView (WebNSViewDetails)
    272 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
    273 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
    274 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView;
    275 - (NSRect)_dirtyRect;
    276 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
    277 - (BOOL)_drawnByAncestor;
    278 - (void)_invalidateGStatesForTree;
    279 - (void)_propagateDirtyRectsToOpaqueAncestors;
    280 - (void)_windowChangedKeyState;
    281 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
    282 - (void)_updateLayerGeometryFromView;
    283 #endif
    284 @end
    285 
    286 #if USE(ACCELERATED_COMPOSITING)
    287 static IMP oldSetNeedsDisplayInRectIMP;
    288 
    289 static void setNeedsDisplayInRect(NSView *self, SEL cmd, NSRect invalidRect)
    290 {
    291     if (![self _drawnByAncestor]) {
    292         oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
    293         return;
    294     }
    295 
    296     static Class webFrameViewClass = [WebFrameView class];
    297     WebFrameView *enclosingWebFrameView = (WebFrameView *)self;
    298     while (enclosingWebFrameView && ![enclosingWebFrameView isKindOfClass:webFrameViewClass])
    299         enclosingWebFrameView = (WebFrameView *)[enclosingWebFrameView superview];
    300 
    301     if (!enclosingWebFrameView) {
    302         oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
    303         return;
    304     }
    305 
    306     Frame* coreFrame = core([enclosingWebFrameView webFrame]);
    307     FrameView* frameView = coreFrame ? coreFrame->view() : 0;
    308     if (!frameView || !frameView->isEnclosedInCompositingLayer()) {
    309         oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
    310         return;
    311     }
    312 
    313     NSRect invalidRectInWebFrameViewCoordinates = [enclosingWebFrameView convertRect:invalidRect fromView:self];
    314     IntRect invalidRectInFrameViewCoordinates(invalidRectInWebFrameViewCoordinates);
    315     if (![enclosingWebFrameView isFlipped])
    316         invalidRectInFrameViewCoordinates.setY(frameView->frameRect().size().height() - invalidRectInFrameViewCoordinates.maxY());
    317 
    318     frameView->invalidateRect(invalidRectInFrameViewCoordinates);
    319 }
    320 #endif // USE(ACCELERATED_COMPOSITING)
    321 
    322 @interface NSApplication (WebNSApplicationDetails)
    323 - (void)speakString:(NSString *)string;
    324 @end
    325 
    326 @interface NSWindow (WebNSWindowDetails)
    327 - (id)_newFirstResponderAfterResigning;
    328 @end
    329 
    330 @interface NSAttributedString (WebNSAttributedStringDetails)
    331 - (id)_initWithDOMRange:(DOMRange *)range;
    332 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
    333 @end
    334 
    335 @interface NSSpellChecker (WebNSSpellCheckerDetails)
    336 - (void)learnWord:(NSString *)word;
    337 @end
    338 
    339 // By imaging to a width a little wider than the available pixels,
    340 // thin pages will be scaled down a little, matching the way they
    341 // print in IE and Camino. This lets them use fewer sheets than they
    342 // would otherwise, which is presumably why other browsers do this.
    343 // Wide pages will be scaled down more than this.
    344 const float _WebHTMLViewPrintingMinimumShrinkFactor = 1.25;
    345 
    346 // This number determines how small we are willing to reduce the page content
    347 // in order to accommodate the widest line. If the page would have to be
    348 // reduced smaller to make the widest line fit, we just clip instead (this
    349 // behavior matches MacIE and Mozilla, at least)
    350 const float _WebHTMLViewPrintingMaximumShrinkFactor = 2;
    351 
    352 #define AUTOSCROLL_INTERVAL             0.1f
    353 
    354 // Any non-zero value will do, but using something recognizable might help us debug some day.
    355 #define TRACKING_RECT_TAG 0xBADFACE
    356 
    357 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
    358 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
    359 
    360 #define STANDARD_WEIGHT 5
    361 #define MIN_BOLD_WEIGHT 7
    362 #define STANDARD_BOLD_WEIGHT 9
    363 
    364 // Fake URL scheme.
    365 #define WebDataProtocolScheme @"webkit-fake-url"
    366 
    367 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
    368 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
    369 @interface WebCoreScrollView : NSScrollView
    370 @end
    371 
    372 @implementation WebCoreScrollView
    373 @end
    374 
    375 // We need this to be able to safely reference the CachedImage for the promised drag data
    376 static CachedResourceClient* promisedDataClient()
    377 {
    378     static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
    379     return staticCachedResourceClient;
    380 }
    381 
    382 @interface WebHTMLView (WebHTMLViewFileInternal)
    383 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
    384 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
    385 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
    386 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
    387 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
    388 - (void)_removeMouseMovedObserverUnconditionally;
    389 - (void)_removeSuperviewObservers;
    390 - (void)_removeWindowObservers;
    391 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
    392 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
    393 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
    394 - (DOMRange *)_selectedRange;
    395 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
    396 - (NSView *)_hitViewForEvent:(NSEvent *)event;
    397 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
    398 - (DOMRange *)_documentRange;
    399 - (void)_setMouseDownEvent:(NSEvent *)event;
    400 - (WebHTMLView *)_topHTMLView;
    401 - (BOOL)_isTopHTMLView;
    402 - (void)_web_setPrintingModeRecursive;
    403 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
    404 - (void)_web_clearPrintingModeRecursive;
    405 @end
    406 
    407 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    408 
    409 @interface WebHTMLView (WebHTMLViewTextCheckingInternal)
    410 - (void)orderFrontSubstitutionsPanel:(id)sender;
    411 - (BOOL)smartInsertDeleteEnabled;
    412 - (void)setSmartInsertDeleteEnabled:(BOOL)flag;
    413 - (void)toggleSmartInsertDelete:(id)sender;
    414 - (BOOL)isAutomaticQuoteSubstitutionEnabled;
    415 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag;
    416 - (void)toggleAutomaticQuoteSubstitution:(id)sender;
    417 - (BOOL)isAutomaticLinkDetectionEnabled;
    418 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag;
    419 - (void)toggleAutomaticLinkDetection:(id)sender;
    420 - (BOOL)isAutomaticDashSubstitutionEnabled;
    421 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag;
    422 - (void)toggleAutomaticDashSubstitution:(id)sender;
    423 - (BOOL)isAutomaticTextReplacementEnabled;
    424 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag;
    425 - (void)toggleAutomaticTextReplacement:(id)sender;
    426 - (BOOL)isAutomaticSpellingCorrectionEnabled;
    427 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag;
    428 - (void)toggleAutomaticSpellingCorrection:(id)sender;
    429 @end
    430 
    431 #endif
    432 
    433 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
    434 - (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageWidth logicalHeight:(float)minPageHeight maximumPageLogicalWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent;
    435 - (void)_updateSecureInputState;
    436 @end
    437 
    438 @class NSTextInputContext;
    439 @interface NSResponder (AppKitDetails)
    440 - (NSTextInputContext *)inputContext;
    441 @end
    442 
    443 @interface NSObject (NSTextInputContextDetails)
    444 - (BOOL)wantsToHandleMouseEvents;
    445 - (BOOL)handleMouseEvent:(NSEvent *)event;
    446 @end
    447 
    448 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
    449 - (void)_updateSelectionForInputManager;
    450 @end
    451 
    452 @interface WebHTMLView (WebEditingStyleSupport)
    453 - (DOMCSSStyleDeclaration *)_emptyStyle;
    454 - (NSString *)_colorAsString:(NSColor *)color;
    455 @end
    456 
    457 @interface NSView (WebHTMLViewFileInternal)
    458 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
    459 @end
    460 
    461 @interface NSMutableDictionary (WebHTMLViewFileInternal)
    462 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
    463 @end
    464 
    465 struct WebHTMLViewInterpretKeyEventsParameters {
    466     KeyboardEvent* event;
    467     bool eventInterpretationHadSideEffects;
    468     bool shouldSaveCommands;
    469     bool consumedByIM;
    470     bool executingSavedKeypressCommands;
    471 };
    472 
    473 @interface WebHTMLViewPrivate : NSObject {
    474 @public
    475     BOOL closed;
    476     BOOL ignoringMouseDraggedEvents;
    477     BOOL printing;
    478     BOOL paginateScreenContent;
    479     BOOL observingMouseMovedNotifications;
    480     BOOL observingSuperviewNotifications;
    481     BOOL observingWindowNotifications;
    482 
    483     id savedSubviews;
    484     BOOL subviewsSetAside;
    485 
    486 #if USE(ACCELERATED_COMPOSITING)
    487     NSView *layerHostingView;
    488     BOOL drawingIntoLayer;
    489 #endif
    490 
    491     NSEvent *mouseDownEvent; // Kept after handling the event.
    492     BOOL handlingMouseDownEvent;
    493     NSEvent *keyDownEvent; // Kept after handling the event.
    494 
    495     // A WebHTMLView has a single input context, but we return nil when in non-editable content to avoid making input methods do their work.
    496     // This state is saved each time selection changes, because computing it causes style recalc, which is not always safe to do.
    497     BOOL exposeInputContext;
    498 
    499     // Track whether the view has set a secure input state.
    500     BOOL isInSecureInputState;
    501 
    502     BOOL _forceUpdateSecureInputState;
    503 
    504     NSPoint lastScrollPosition;
    505 #ifndef BUILDING_ON_TIGER
    506     BOOL inScrollPositionChanged;
    507 #endif
    508 
    509     WebPluginController *pluginController;
    510 
    511     NSString *toolTip;
    512     NSToolTipTag lastToolTipTag;
    513     id trackingRectOwner;
    514     void *trackingRectUserData;
    515 
    516     NSTimer *autoscrollTimer;
    517     NSEvent *autoscrollTriggerEvent;
    518 
    519     NSArray *pageRects;
    520 
    521     NSMutableDictionary *highlighters;
    522 
    523 #ifdef BUILDING_ON_TIGER
    524     BOOL nextResponderDisabledOnce;
    525 #endif
    526 
    527     WebTextCompletionController *completionController;
    528 
    529     BOOL transparentBackground;
    530 
    531     WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters;
    532 
    533     WebDataSource *dataSource;
    534     WebCore::CachedImage* promisedDragTIFFDataSource;
    535 
    536     CFRunLoopTimerRef updateMouseoverTimer;
    537 
    538     SEL selectorForDoCommandBySelector;
    539 
    540 #ifndef NDEBUG
    541     BOOL enumeratingSubviews;
    542 #endif
    543 }
    544 - (void)clear;
    545 @end
    546 
    547 static NSCellStateValue kit(TriState state)
    548 {
    549     switch (state) {
    550         case FalseTriState:
    551             return NSOffState;
    552         case TrueTriState:
    553             return NSOnState;
    554         case MixedTriState:
    555             return NSMixedState;
    556     }
    557     ASSERT_NOT_REACHED();
    558     return NSOffState;
    559 }
    560 
    561 static FindOptions coreOptions(WebFindOptions options)
    562 {
    563     return (options & WebFindOptionsCaseInsensitive ? CaseInsensitive : 0)
    564         | (options & WebFindOptionsAtWordStarts ? AtWordStarts : 0)
    565         | (options & WebFindOptionsTreatMedialCapitalAsWordStart ? TreatMedialCapitalAsWordStart : 0)
    566         | (options & WebFindOptionsBackwards ? Backwards : 0)
    567         | (options & WebFindOptionsWrapAround ? WrapAround : 0)
    568         | (options & WebFindOptionsStartInSelection ? StartInSelection : 0);
    569 }
    570 
    571 @implementation WebHTMLViewPrivate
    572 
    573 + (void)initialize
    574 {
    575     JSC::initializeThreading();
    576     WTF::initializeMainThreadToProcessMainThread();
    577 #ifndef BUILDING_ON_TIGER
    578     WebCoreObjCFinalizeOnMainThread(self);
    579 #endif
    580 
    581 #ifndef BUILDING_ON_TIGER
    582     if (!oldSetCursorForMouseLocationIMP) {
    583         Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
    584         ASSERT(setCursorMethod);
    585         oldSetCursorForMouseLocationIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
    586         ASSERT(oldSetCursorForMouseLocationIMP);
    587     }
    588 
    589 #if USE(ACCELERATED_COMPOSITING)
    590     if (!oldSetNeedsDisplayInRectIMP) {
    591         Method setNeedsDisplayInRectMethod = class_getInstanceMethod([NSView class], @selector(setNeedsDisplayInRect:));
    592         ASSERT(setNeedsDisplayInRectMethod);
    593         oldSetNeedsDisplayInRectIMP = method_setImplementation(setNeedsDisplayInRectMethod, (IMP)setNeedsDisplayInRect);
    594         ASSERT(oldSetNeedsDisplayInRectIMP);
    595     }
    596 #endif // USE(ACCELERATED_COMPOSITING)
    597 
    598 #else // defined(BUILDING_ON_TIGER)
    599     if (!oldSetCursorIMP) {
    600         Method setCursorMethod = class_getInstanceMethod([NSCursor class], @selector(set));
    601         ASSERT(setCursorMethod);
    602         oldSetCursorIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
    603         ASSERT(oldSetCursorIMP);
    604     }
    605     if (!oldResetCursorRectsIMP) {
    606         Method resetCursorRectsMethod = class_getInstanceMethod([NSWindow class], @selector(resetCursorRects));
    607         ASSERT(resetCursorRectsMethod);
    608         oldResetCursorRectsIMP = method_setImplementation(resetCursorRectsMethod, (IMP)resetCursorRects);
    609         ASSERT(oldResetCursorRectsIMP);
    610     }
    611 #endif
    612 
    613 }
    614 
    615 - (void)dealloc
    616 {
    617     if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self))
    618         return;
    619 
    620     ASSERT(!autoscrollTimer);
    621     ASSERT(!autoscrollTriggerEvent);
    622     ASSERT(!updateMouseoverTimer);
    623 
    624     [mouseDownEvent release];
    625     [keyDownEvent release];
    626     [pluginController release];
    627     [toolTip release];
    628     [completionController release];
    629     [dataSource release];
    630     [highlighters release];
    631     if (promisedDragTIFFDataSource)
    632         promisedDragTIFFDataSource->removeClient(promisedDataClient());
    633 
    634     [super dealloc];
    635 }
    636 
    637 - (void)finalize
    638 {
    639     ASSERT_MAIN_THREAD();
    640 
    641     if (promisedDragTIFFDataSource)
    642         promisedDragTIFFDataSource->removeClient(promisedDataClient());
    643 
    644     [super finalize];
    645 }
    646 
    647 - (void)clear
    648 {
    649     [mouseDownEvent release];
    650     [keyDownEvent release];
    651     [pluginController release];
    652     [toolTip release];
    653     [completionController release];
    654     [dataSource release];
    655     [highlighters release];
    656     if (promisedDragTIFFDataSource)
    657         promisedDragTIFFDataSource->removeClient(promisedDataClient());
    658 
    659     mouseDownEvent = nil;
    660     keyDownEvent = nil;
    661     pluginController = nil;
    662     toolTip = nil;
    663     completionController = nil;
    664     dataSource = nil;
    665     highlighters = nil;
    666     promisedDragTIFFDataSource = 0;
    667 
    668 #if USE(ACCELERATED_COMPOSITING)
    669     layerHostingView = nil;
    670 #endif
    671 }
    672 
    673 @end
    674 
    675 @implementation WebHTMLView (WebHTMLViewFileInternal)
    676 
    677 - (DOMRange *)_documentRange
    678 {
    679     return [[[self _frame] DOMDocument] _documentRange];
    680 }
    681 
    682 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
    683 {
    684     NSEnumerator *enumerator = [paths objectEnumerator];
    685     NSString *path;
    686 
    687     while ((path = [enumerator nextObject]) != nil) {
    688         NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
    689         if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
    690             return YES;
    691     }
    692 
    693     return NO;
    694 }
    695 
    696 - (WebDataSource *)_dataSource
    697 {
    698     return _private->dataSource;
    699 }
    700 
    701 - (WebView *)_webView
    702 {
    703     return [_private->dataSource _webView];
    704 }
    705 
    706 - (WebFrameView *)_frameView
    707 {
    708     return [[_private->dataSource webFrame] frameView];
    709 }
    710 
    711 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
    712 {
    713     DOMDocumentFragment *fragment;
    714     NSEnumerator *enumerator = [paths objectEnumerator];
    715     NSMutableArray *domNodes = [[NSMutableArray alloc] init];
    716     NSString *path;
    717 
    718     while ((path = [enumerator nextObject]) != nil) {
    719         // Non-image file types; _web_userVisibleString is appropriate here because this will
    720         // be pasted as visible text.
    721         NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
    722         [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
    723     }
    724 
    725     fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes];
    726 
    727     [domNodes release];
    728 
    729     return [fragment firstChild] != nil ? fragment : nil;
    730 }
    731 
    732 + (NSArray *)_excludedElementsForAttributedStringConversion
    733 {
    734     static NSArray *elements = nil;
    735     if (elements == nil) {
    736         elements = [[NSArray alloc] initWithObjects:
    737             // Omit style since we want style to be inline so the fragment can be easily inserted.
    738             @"style",
    739             // Omit xml so the result is not XHTML.
    740             @"xml",
    741             // Omit tags that will get stripped when converted to a fragment anyway.
    742             @"doctype", @"html", @"head", @"body",
    743             // Omit deprecated tags.
    744             @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
    745             // Omit object so no file attachments are part of the fragment.
    746             @"object", nil];
    747         CFRetain(elements);
    748     }
    749     return elements;
    750 }
    751 
    752 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
    753 {
    754     CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
    755     NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
    756     CFRelease(UUIDRef);
    757     NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
    758     CFRelease(UUIDString);
    759 
    760     return URL;
    761 }
    762 
    763 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
    764                                                inContext:(DOMRange *)context
    765                                           allowPlainText:(BOOL)allowPlainText
    766 {
    767     NSArray *types = [pasteboard types];
    768     DOMDocumentFragment *fragment = nil;
    769 
    770     if ([types containsObject:WebArchivePboardType] &&
    771         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    772                                                   forType:WebArchivePboardType
    773                                                 inContext:context
    774                                              subresources:0]))
    775         return fragment;
    776 
    777     if ([types containsObject:NSFilenamesPboardType] &&
    778         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    779                                                   forType:NSFilenamesPboardType
    780                                                 inContext:context
    781                                              subresources:0]))
    782         return fragment;
    783 
    784     if ([types containsObject:NSHTMLPboardType] &&
    785         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    786                                                   forType:NSHTMLPboardType
    787                                                 inContext:context
    788                                              subresources:0]))
    789         return fragment;
    790 
    791     if ([types containsObject:NSRTFDPboardType] &&
    792         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    793                                                   forType:NSRTFDPboardType
    794                                                 inContext:context
    795                                              subresources:0]))
    796         return fragment;
    797 
    798     if ([types containsObject:NSRTFPboardType] &&
    799         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    800                                                   forType:NSRTFPboardType
    801                                                 inContext:context
    802                                              subresources:0]))
    803         return fragment;
    804 
    805     if ([types containsObject:NSTIFFPboardType] &&
    806         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    807                                                   forType:NSTIFFPboardType
    808                                                 inContext:context
    809                                              subresources:0]))
    810         return fragment;
    811 
    812     if ([types containsObject:NSPDFPboardType] &&
    813         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    814                                                   forType:NSPDFPboardType
    815                                                 inContext:context
    816                                              subresources:0]))
    817         return fragment;
    818 
    819 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
    820     if ([types containsObject:NSPICTPboardType] &&
    821         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    822                                                   forType:NSPICTPboardType
    823                                                 inContext:context
    824                                              subresources:0]))
    825         return fragment;
    826 #endif
    827 
    828     // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
    829     // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
    830     if ([types containsObject:(NSString*)kUTTypePNG] &&
    831         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    832                                                   forType:(NSString*)kUTTypePNG
    833                                                 inContext:context
    834                                              subresources:0]))
    835         return fragment;
    836 
    837     if ([types containsObject:NSURLPboardType] &&
    838         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    839                                                   forType:NSURLPboardType
    840                                                 inContext:context
    841                                              subresources:0]))
    842         return fragment;
    843 
    844     if (allowPlainText && [types containsObject:NSStringPboardType] &&
    845         (fragment = [self _documentFragmentFromPasteboard:pasteboard
    846                                                   forType:NSStringPboardType
    847                                                 inContext:context
    848                                              subresources:0])) {
    849         return fragment;
    850     }
    851 
    852     return nil;
    853 }
    854 
    855 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
    856 {
    857     NSArray *types = [pasteboard types];
    858 
    859     if ([types containsObject:NSStringPboardType])
    860         return [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping];
    861 
    862     NSAttributedString *attributedString = nil;
    863     NSString *string;
    864 
    865     if ([types containsObject:NSRTFDPboardType])
    866         attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
    867     if (attributedString == nil && [types containsObject:NSRTFPboardType])
    868         attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
    869     if (attributedString != nil) {
    870         string = [[attributedString string] copy];
    871         [attributedString release];
    872         return [string autorelease];
    873     }
    874 
    875     if ([types containsObject:NSFilenamesPboardType]) {
    876         string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
    877         if (string != nil)
    878             return string;
    879     }
    880 
    881     NSURL *URL;
    882 
    883     if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
    884         string = [URL _web_userVisibleString];
    885         if ([string length] > 0)
    886             return string;
    887     }
    888 
    889     return nil;
    890 }
    891 
    892 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
    893 {
    894     WebView *webView = [[self _webView] retain];
    895     [webView _setInsertionPasteboard:pasteboard];
    896 
    897     DOMRange *range = [self _selectedRange];
    898     Frame* coreFrame = core([self _frame]);
    899 
    900 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
    901     DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
    902     if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted])
    903         coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
    904 #else
    905     // Mail is ignoring the frament passed to the delegate and creates a new one.
    906     // We want to avoid creating the fragment twice.
    907     if (applicationIsAppleMail()) {
    908         if ([self _shouldInsertFragment:nil replacingDOMRange:range givenAction:WebViewInsertActionPasted]) {
    909             DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
    910             if (fragment)
    911                 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
    912         }
    913     } else {
    914         DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
    915         if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted])
    916             coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
    917     }
    918 #endif
    919     [webView _setInsertionPasteboard:nil];
    920     [webView release];
    921 }
    922 
    923 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
    924 {
    925     WebView *webView = [[self _webView] retain];
    926     [webView _setInsertionPasteboard:pasteboard];
    927 
    928     NSString *text = [self _plainTextFromPasteboard:pasteboard];
    929     if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
    930         [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
    931 
    932     [webView _setInsertionPasteboard:nil];
    933     [webView release];
    934 }
    935 
    936 // This method is needed to support Mac OS X services.
    937 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
    938 {
    939     Frame* coreFrame = core([self _frame]);
    940     if (!coreFrame)
    941         return NO;
    942     if (coreFrame->selection()->isContentRichlyEditable())
    943         [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
    944     else
    945         [self _pasteAsPlainTextWithPasteboard:pasteboard];
    946     return YES;
    947 }
    948 
    949 - (void)_removeMouseMovedObserverUnconditionally
    950 {
    951     if (!_private || !_private->observingMouseMovedNotifications)
    952         return;
    953 
    954     [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil];
    955     _private->observingMouseMovedNotifications = false;
    956 }
    957 
    958 - (void)_removeSuperviewObservers
    959 {
    960     if (!_private || !_private->observingSuperviewNotifications)
    961         return;
    962 
    963     NSView *superview = [self superview];
    964     if (!superview || ![self window])
    965         return;
    966 
    967     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    968     [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview];
    969     [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview];
    970 
    971     _private->observingSuperviewNotifications = false;
    972 }
    973 
    974 - (void)_removeWindowObservers
    975 {
    976     if (!_private->observingWindowNotifications)
    977         return;
    978 
    979     NSWindow *window = [self window];
    980     if (!window)
    981         return;
    982 
    983     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    984     [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
    985     [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
    986     [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window];
    987 
    988     _private->observingWindowNotifications = false;
    989 }
    990 
    991 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
    992 {
    993     WebView *webView = [self _webView];
    994     DOMNode *child = [fragment firstChild];
    995     if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
    996         return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
    997     return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
    998 }
    999 
   1000 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
   1001 {
   1002     WebView *webView = [self _webView];
   1003     return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
   1004 }
   1005 
   1006 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
   1007 {
   1008     return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
   1009 }
   1010 
   1011 - (DOMRange *)_selectedRange
   1012 {
   1013     Frame* coreFrame = core([self _frame]);
   1014     return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil;
   1015 }
   1016 
   1017 - (BOOL)_shouldDeleteRange:(DOMRange *)range
   1018 {
   1019     Frame* coreFrame = core([self _frame]);
   1020     return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
   1021 }
   1022 
   1023 - (NSView *)_hitViewForEvent:(NSEvent *)event
   1024 {
   1025     // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.
   1026     // Callers of this method, however, want to query the deepest view instead.
   1027     forceNSViewHitTest = YES;
   1028     NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
   1029     forceNSViewHitTest = NO;
   1030     return hitView;
   1031 }
   1032 
   1033 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
   1034 {
   1035     // Put HTML on the pasteboard.
   1036     if ([types containsObject:WebArchivePboardType]) {
   1037         if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) {
   1038             if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0)
   1039                 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType];
   1040         }
   1041     }
   1042 
   1043     // Put the attributed string on the pasteboard (RTF/RTFD format).
   1044     if ([types containsObject:NSRTFDPboardType]) {
   1045         if (attributedString == nil) {
   1046             attributedString = [self selectedAttributedString];
   1047         }
   1048         NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
   1049         [pasteboard setData:RTFDData forType:NSRTFDPboardType];
   1050     }
   1051     if ([types containsObject:NSRTFPboardType]) {
   1052         if (!attributedString)
   1053             attributedString = [self selectedAttributedString];
   1054         if ([attributedString containsAttachments])
   1055             attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
   1056         NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
   1057         [pasteboard setData:RTFData forType:NSRTFPboardType];
   1058     }
   1059 
   1060     // Put plain string on the pasteboard.
   1061     if ([types containsObject:NSStringPboardType]) {
   1062         // Map &nbsp; to a plain old space because this is better for source code, other browsers do it,
   1063         // and because HTML forces you to do this any time you want two spaces in a row.
   1064         NSMutableString *s = [[self selectedString] mutableCopy];
   1065         const unichar NonBreakingSpaceCharacter = 0xA0;
   1066         NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
   1067         [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
   1068         [pasteboard setString:s forType:NSStringPboardType];
   1069         [s release];
   1070     }
   1071 
   1072     if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
   1073         [pasteboard setData:nil forType:WebSmartPastePboardType];
   1074     }
   1075 }
   1076 
   1077 - (void)_setMouseDownEvent:(NSEvent *)event
   1078 {
   1079     ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
   1080 
   1081     if (event == _private->mouseDownEvent)
   1082         return;
   1083 
   1084     [event retain];
   1085     [_private->mouseDownEvent release];
   1086     _private->mouseDownEvent = event;
   1087 }
   1088 
   1089 - (void)_cancelUpdateMouseoverTimer
   1090 {
   1091     if (_private->updateMouseoverTimer) {
   1092         CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
   1093         CFRelease(_private->updateMouseoverTimer);
   1094         _private->updateMouseoverTimer = NULL;
   1095     }
   1096 }
   1097 
   1098 - (WebHTMLView *)_topHTMLView
   1099 {
   1100     // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
   1101     WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
   1102     ASSERT(!view || [view isKindOfClass:[WebHTMLView class]]);
   1103     return view;
   1104 }
   1105 
   1106 - (BOOL)_isTopHTMLView
   1107 {
   1108     // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
   1109     return self == [self _topHTMLView];
   1110 }
   1111 
   1112 - (void)_web_setPrintingModeRecursive
   1113 {
   1114     [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
   1115 
   1116 #ifndef NDEBUG
   1117     _private->enumeratingSubviews = YES;
   1118 #endif
   1119 
   1120     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
   1121 
   1122     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
   1123 
   1124     unsigned count = [descendantWebHTMLViews count];
   1125     for (unsigned i = 0; i < count; ++i)
   1126         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
   1127 
   1128     [descendantWebHTMLViews release];
   1129 
   1130 #ifndef NDEBUG
   1131     _private->enumeratingSubviews = NO;
   1132 #endif
   1133 }
   1134 
   1135 - (void)_web_clearPrintingModeRecursive
   1136 {
   1137     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
   1138 
   1139 #ifndef NDEBUG
   1140     _private->enumeratingSubviews = YES;
   1141 #endif
   1142 
   1143     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
   1144 
   1145     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
   1146 
   1147     unsigned count = [descendantWebHTMLViews count];
   1148     for (unsigned i = 0; i < count; ++i)
   1149         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
   1150 
   1151     [descendantWebHTMLViews release];
   1152 
   1153 #ifndef NDEBUG
   1154     _private->enumeratingSubviews = NO;
   1155 #endif
   1156 }
   1157 
   1158 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize
   1159 {
   1160     [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
   1161 
   1162 #ifndef NDEBUG
   1163     _private->enumeratingSubviews = YES;
   1164 #endif
   1165 
   1166     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
   1167 
   1168     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
   1169 
   1170     unsigned count = [descendantWebHTMLViews count];
   1171     for (unsigned i = 0; i < count; ++i)
   1172         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
   1173 
   1174     [descendantWebHTMLViews release];
   1175 
   1176 #ifndef NDEBUG
   1177     _private->enumeratingSubviews = NO;
   1178 #endif
   1179 }
   1180 
   1181 @end
   1182 
   1183 @implementation WebHTMLView (WebPrivate)
   1184 
   1185 + (NSArray *)supportedMIMETypes
   1186 {
   1187     return [WebHTMLRepresentation supportedMIMETypes];
   1188 }
   1189 
   1190 + (NSArray *)supportedImageMIMETypes
   1191 {
   1192     return [WebHTMLRepresentation supportedImageMIMETypes];
   1193 }
   1194 
   1195 + (NSArray *)supportedNonImageMIMETypes
   1196 {
   1197     return [WebHTMLRepresentation supportedNonImageMIMETypes];
   1198 }
   1199 
   1200 + (NSArray *)unsupportedTextMIMETypes
   1201 {
   1202     return [WebHTMLRepresentation unsupportedTextMIMETypes];
   1203 }
   1204 
   1205 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
   1206 {
   1207     // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged
   1208     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
   1209         location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
   1210         modifierFlags:[flagsChangedEvent modifierFlags]
   1211         timestamp:[flagsChangedEvent timestamp]
   1212         windowNumber:[flagsChangedEvent windowNumber]
   1213         context:[flagsChangedEvent context]
   1214         eventNumber:0 clickCount:0 pressure:0];
   1215 
   1216     // Pretend it's a mouse move.
   1217     [[NSNotificationCenter defaultCenter]
   1218         postNotificationName:WKMouseMovedNotification() object:self
   1219         userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
   1220 }
   1221 
   1222 - (id)_bridge
   1223 {
   1224     // This method exists to maintain compatibility with Leopard's Dictionary.app, since it
   1225     // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:.
   1226     // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160>
   1227     return [self _frame];
   1228 }
   1229 
   1230 - (void)_updateMouseoverWithFakeEvent
   1231 {
   1232     [self _cancelUpdateMouseoverTimer];
   1233 
   1234     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
   1235         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
   1236         modifierFlags:[[NSApp currentEvent] modifierFlags]
   1237         timestamp:[NSDate timeIntervalSinceReferenceDate]
   1238         windowNumber:[[self window] windowNumber]
   1239         context:[[NSApp currentEvent] context]
   1240         eventNumber:0 clickCount:0 pressure:0];
   1241 
   1242     [self _updateMouseoverWithEvent:fakeEvent];
   1243 }
   1244 
   1245 static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
   1246 {
   1247     WebHTMLView *view = (WebHTMLView *)info;
   1248 
   1249     [view _updateMouseoverWithFakeEvent];
   1250 }
   1251 
   1252 - (void)_frameOrBoundsChanged
   1253 {
   1254     WebView *webView = [self _webView];
   1255     WebDynamicScrollBarsView *scrollView = [[[webView mainFrame] frameView] _scrollView];
   1256 
   1257     NSPoint origin = [[self superview] bounds].origin;
   1258     if (!NSEqualPoints(_private->lastScrollPosition, origin) && ![scrollView inProgrammaticScroll]) {
   1259         if (Frame* coreFrame = core([self _frame])) {
   1260             if (FrameView* coreView = coreFrame->view()) {
   1261 #ifndef BUILDING_ON_TIGER
   1262                 _private->inScrollPositionChanged = YES;
   1263 #endif
   1264                 coreView->scrollPositionChangedViaPlatformWidget();
   1265 #ifndef BUILDING_ON_TIGER
   1266                 _private->inScrollPositionChanged = NO;
   1267 #endif
   1268             }
   1269         }
   1270 
   1271         [_private->completionController endRevertingChange:NO moveLeft:NO];
   1272 
   1273         [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
   1274     }
   1275     _private->lastScrollPosition = origin;
   1276 
   1277     if ([self window] && !_private->closed && !_private->updateMouseoverTimer) {
   1278         CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
   1279 
   1280         // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading
   1281         // and scrolling rapidly back to back.
   1282         _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0,
   1283                                                               _updateMouseoverTimerCallback, &context);
   1284         CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode);
   1285     }
   1286 
   1287 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
   1288     [self _updateLayerHostingViewPosition];
   1289 #endif
   1290 }
   1291 
   1292 - (void)_setAsideSubviews
   1293 {
   1294     ASSERT(!_private->subviewsSetAside);
   1295     ASSERT(_private->savedSubviews == nil);
   1296     _private->savedSubviews = _subviews;
   1297 #if USE(ACCELERATED_COMPOSITING)
   1298     // We need to keep the layer-hosting view in the subviews, otherwise the layers flash.
   1299     if (_private->layerHostingView) {
   1300         NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil];
   1301         _subviews = newSubviews;
   1302     } else
   1303         _subviews = nil;
   1304 #else
   1305     _subviews = nil;
   1306 #endif
   1307     _private->subviewsSetAside = YES;
   1308  }
   1309 
   1310  - (void)_restoreSubviews
   1311  {
   1312     ASSERT(_private->subviewsSetAside);
   1313 #if USE(ACCELERATED_COMPOSITING)
   1314     if (_private->layerHostingView) {
   1315         [_subviews release];
   1316         _subviews = _private->savedSubviews;
   1317     } else {
   1318         ASSERT(_subviews == nil);
   1319         _subviews = _private->savedSubviews;
   1320     }
   1321 #else
   1322     ASSERT(_subviews == nil);
   1323     _subviews = _private->savedSubviews;
   1324 #endif
   1325     _private->savedSubviews = nil;
   1326     _private->subviewsSetAside = NO;
   1327 }
   1328 
   1329 #ifndef NDEBUG
   1330 
   1331 - (void)didAddSubview:(NSView *)subview
   1332 {
   1333     if (_private->enumeratingSubviews)
   1334         LOG(View, "A view of class %s was added during subview enumeration for layout or printing mode change. This view might paint without first receiving layout.", object_getClassName([subview class]));
   1335 }
   1336 #endif
   1337 
   1338 #ifdef BUILDING_ON_TIGER
   1339 
   1340 // This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
   1341 // That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty
   1342 // before doing it. As a compromise, when we're opaque we do the layout only when actually asked to
   1343 // draw, but when we're transparent we do the layout at this stage so views behind us know that they
   1344 // need to be redrawn (in case the layout causes some things to get dirtied).
   1345 - (void)_propagateDirtyRectsToOpaqueAncestors
   1346 {
   1347     if (![[self _webView] drawsBackground])
   1348         [self _web_updateLayoutAndStyleIfNeededRecursive];
   1349     [super _propagateDirtyRectsToOpaqueAncestors];
   1350 }
   1351 
   1352 #else
   1353 
   1354 - (void)viewWillDraw
   1355 {
   1356     // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
   1357     // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
   1358     // once the FIXME in _isTopHTMLView is fixed.
   1359     if (_private->dataSource && [self _isTopHTMLView])
   1360         [self _web_updateLayoutAndStyleIfNeededRecursive];
   1361     [super viewWillDraw];
   1362 }
   1363 
   1364 #endif
   1365 
   1366 // Don't let AppKit even draw subviews. We take care of that.
   1367 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
   1368 {
   1369     // This helps when we print as part of a larger print process.
   1370     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
   1371     BOOL wasInPrintingMode = _private->printing;
   1372     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
   1373     if (isPrinting) {
   1374         if (!wasInPrintingMode)
   1375             [self _web_setPrintingModeRecursive];
   1376 #ifndef BUILDING_ON_TIGER
   1377         else
   1378             [self _web_updateLayoutAndStyleIfNeededRecursive];
   1379 #endif
   1380     } else if (wasInPrintingMode)
   1381         [self _web_clearPrintingModeRecursive];
   1382 
   1383 #ifndef BUILDING_ON_TIGER
   1384     // There are known cases where -viewWillDraw is not called on all views being drawn.
   1385     // See <rdar://problem/6964278> for example. Performing layout at this point prevents us from
   1386     // trying to paint without layout (which WebCore now refuses to do, instead bailing out without
   1387     // drawing at all), but we may still fail to update any regions dirtied by the layout which are
   1388     // not already dirty.
   1389     if ([self _needsLayout]) {
   1390         NSInteger rectCount;
   1391         [self getRectsBeingDrawn:0 count:&rectCount];
   1392         if (rectCount) {
   1393             LOG_ERROR("View needs layout. Either -viewWillDraw wasn't called or layout was invalidated during the display operation. Performing layout now.");
   1394             [self _web_updateLayoutAndStyleIfNeededRecursive];
   1395         }
   1396     }
   1397 #else
   1398     // Because Tiger does not have viewWillDraw we need to do layout here.
   1399     [self _web_updateLayoutAndStyleIfNeededRecursive];
   1400     [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
   1401 #endif
   1402 
   1403     [self _setAsideSubviews];
   1404     [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
   1405     [self _restoreSubviews];
   1406 
   1407     if (wasInPrintingMode != isPrinting) {
   1408         if (wasInPrintingMode)
   1409             [self _web_setPrintingModeRecursive];
   1410         else
   1411             [self _web_clearPrintingModeRecursive];
   1412     }
   1413 }
   1414 
   1415 // Don't let AppKit even draw subviews. We take care of that.
   1416 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
   1417 {
   1418     BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
   1419 
   1420     BOOL wasInPrintingMode = _private->printing;
   1421     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
   1422 
   1423     if (needToSetAsideSubviews) {
   1424         // This helps when we print as part of a larger print process.
   1425         // If the WebHTMLView itself is what we're printing, then we will never have to do this.
   1426         if (isPrinting) {
   1427             if (!wasInPrintingMode)
   1428                 [self _web_setPrintingModeRecursive];
   1429 #ifndef BUILDING_ON_TIGER
   1430             else
   1431                 [self _web_updateLayoutAndStyleIfNeededRecursive];
   1432 #endif
   1433         } else if (wasInPrintingMode)
   1434             [self _web_clearPrintingModeRecursive];
   1435 
   1436 #ifdef BUILDING_ON_TIGER
   1437 
   1438         // Because Tiger does not have viewWillDraw we need to do layout here.
   1439         NSRect boundsBeforeLayout = [self bounds];
   1440         if (!NSIsEmptyRect(visRect))
   1441             [self _web_updateLayoutAndStyleIfNeededRecursive];
   1442 
   1443         // If layout changes the view's bounds, then we need to recompute the visRect.
   1444         // That's because the visRect passed to us was based on the bounds at the time
   1445         // we were called. This method is only displayed to draw "all", so it's safe
   1446         // to just call visibleRect to compute the entire rectangle.
   1447         if (!NSEqualRects(boundsBeforeLayout, [self bounds]))
   1448             visRect = [self visibleRect];
   1449 
   1450 #endif
   1451 
   1452         [self _setAsideSubviews];
   1453     }
   1454 
   1455     [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
   1456 
   1457     if (needToSetAsideSubviews) {
   1458         if (wasInPrintingMode != isPrinting) {
   1459             if (wasInPrintingMode)
   1460                 [self _web_setPrintingModeRecursive];
   1461             else
   1462                 [self _web_clearPrintingModeRecursive];
   1463         }
   1464 
   1465         [self _restoreSubviews];
   1466     }
   1467 }
   1468 
   1469 // Don't let AppKit even draw subviews. We take care of that.
   1470 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView
   1471 {
   1472 #ifdef BUILDING_ON_TIGER
   1473     // Because Tiger does not have viewWillDraw we need to do layout here.
   1474     [self _web_updateLayoutAndStyleIfNeededRecursive];
   1475 #endif
   1476 
   1477     [self _setAsideSubviews];
   1478     [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView];
   1479     [self _restoreSubviews];
   1480 }
   1481 
   1482 - (BOOL)_insideAnotherHTMLView
   1483 {
   1484     return self != [self _topHTMLView];
   1485 }
   1486 
   1487 - (NSView *)hitTest:(NSPoint)point
   1488 {
   1489     // WebHTMLView objects handle all events for objects inside them.
   1490     // To get those events, we prevent hit testing from AppKit.
   1491 
   1492     // But there are three exceptions to this:
   1493     //   1) For right mouse clicks and control clicks we don't yet have an implementation
   1494     //      that works for nested views, so we let the hit testing go through the
   1495     //      standard NSView code path (needs to be fixed, see bug 4361618).
   1496     //   2) Java depends on doing a hit test inside it's mouse moved handling,
   1497     //      so we let the hit testing go through the standard NSView code path
   1498     //      when the current event is a mouse move (except when we are calling
   1499     //      from _updateMouseoverWithEvent, so we have to use a global,
   1500     //      forceWebHTMLViewHitTest, for that)
   1501     //   3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
   1502     //      both need to figure out which view to check with inside the WebHTMLView.
   1503     //      They use a global to change the behavior of hitTest: so they can get the
   1504     //      right view. The global is forceNSViewHitTest and the method they use to
   1505     //      do the hit testing is _hitViewForEvent:. (But this does not work correctly
   1506     //      when there is HTML overlapping the view, see bug 4361626)
   1507     //   4) NSAccessibilityHitTest relies on this for checking the cursor position.
   1508     //      Our check for that is whether the event is NSFlagsChanged.  This works
   1509     //      for VoiceOver's Control-Option-F5 command (move focus to item under cursor)
   1510     //      and Dictionary's Command-Control-D (open dictionary popup for item under cursor).
   1511     //      This is of course a hack.
   1512 
   1513     if (_private->closed)
   1514         return nil;
   1515 
   1516     BOOL captureHitsOnSubviews;
   1517     if (forceNSViewHitTest)
   1518         captureHitsOnSubviews = NO;
   1519     else if (forceWebHTMLViewHitTest)
   1520         captureHitsOnSubviews = YES;
   1521     else {
   1522         // FIXME: Why doesn't this include mouse entered/exited events, or other mouse button events?
   1523         NSEvent *event = [[self window] currentEvent];
   1524         captureHitsOnSubviews = !([event type] == NSMouseMoved
   1525             || [event type] == NSRightMouseDown
   1526             || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
   1527             || [event type] == NSFlagsChanged);
   1528     }
   1529 
   1530     if (!captureHitsOnSubviews) {
   1531         NSView* hitView = [super hitTest:point];
   1532 #if USE(ACCELERATED_COMPOSITING)
   1533         if (_private && hitView == _private->layerHostingView)
   1534             hitView = self;
   1535 #endif
   1536         return hitView;
   1537     }
   1538     if ([[self superview] mouse:point inRect:[self frame]])
   1539         return self;
   1540     return nil;
   1541 }
   1542 
   1543 - (void)_clearLastHitViewIfSelf
   1544 {
   1545     if (lastHitView == self)
   1546         lastHitView = nil;
   1547 }
   1548 
   1549 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
   1550 {
   1551     ASSERT(_private->trackingRectOwner == nil);
   1552     _private->trackingRectOwner = owner;
   1553     _private->trackingRectUserData = data;
   1554     return TRACKING_RECT_TAG;
   1555 }
   1556 
   1557 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
   1558 {
   1559     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
   1560     ASSERT(_private->trackingRectOwner == nil);
   1561     _private->trackingRectOwner = owner;
   1562     _private->trackingRectUserData = data;
   1563     return TRACKING_RECT_TAG;
   1564 }
   1565 
   1566 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
   1567 {
   1568     ASSERT(count == 1);
   1569     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
   1570     ASSERT(_private->trackingRectOwner == nil);
   1571     _private->trackingRectOwner = owner;
   1572     _private->trackingRectUserData = userDataList[0];
   1573     trackingNums[0] = TRACKING_RECT_TAG;
   1574 }
   1575 
   1576 - (void)removeTrackingRect:(NSTrackingRectTag)tag
   1577 {
   1578     if (tag == 0)
   1579         return;
   1580 
   1581     if (_private && (tag == TRACKING_RECT_TAG)) {
   1582         _private->trackingRectOwner = nil;
   1583         return;
   1584     }
   1585 
   1586     if (_private && (tag == _private->lastToolTipTag)) {
   1587         [super removeTrackingRect:tag];
   1588         _private->lastToolTipTag = 0;
   1589         return;
   1590     }
   1591 
   1592     // If any other tracking rect is being removed, we don't know how it was created
   1593     // and it's possible there's a leak involved (see 3500217)
   1594     ASSERT_NOT_REACHED();
   1595 }
   1596 
   1597 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
   1598 {
   1599     int i;
   1600     for (i = 0; i < count; ++i) {
   1601         int tag = tags[i];
   1602         if (tag == 0)
   1603             continue;
   1604         ASSERT(tag == TRACKING_RECT_TAG);
   1605         if (_private != nil) {
   1606             _private->trackingRectOwner = nil;
   1607         }
   1608     }
   1609 }
   1610 
   1611 - (void)_sendToolTipMouseExited
   1612 {
   1613     // Nothing matters except window, trackingNumber, and userData.
   1614     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
   1615         location:NSMakePoint(0, 0)
   1616         modifierFlags:0
   1617         timestamp:0
   1618         windowNumber:[[self window] windowNumber]
   1619         context:NULL
   1620         eventNumber:0
   1621         trackingNumber:TRACKING_RECT_TAG
   1622         userData:_private->trackingRectUserData];
   1623     [_private->trackingRectOwner mouseExited:fakeEvent];
   1624 }
   1625 
   1626 - (void)_sendToolTipMouseEntered
   1627 {
   1628     // Nothing matters except window, trackingNumber, and userData.
   1629     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
   1630         location:NSMakePoint(0, 0)
   1631         modifierFlags:0
   1632         timestamp:0
   1633         windowNumber:[[self window] windowNumber]
   1634         context:NULL
   1635         eventNumber:0
   1636         trackingNumber:TRACKING_RECT_TAG
   1637         userData:_private->trackingRectUserData];
   1638     [_private->trackingRectOwner mouseEntered:fakeEvent];
   1639 }
   1640 
   1641 - (void)_setToolTip:(NSString *)string
   1642 {
   1643     NSString *toolTip = [string length] == 0 ? nil : string;
   1644     NSString *oldToolTip = _private->toolTip;
   1645     if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
   1646         return;
   1647     }
   1648     if (oldToolTip) {
   1649         [self _sendToolTipMouseExited];
   1650         [oldToolTip release];
   1651     }
   1652     _private->toolTip = [toolTip copy];
   1653     if (toolTip) {
   1654         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
   1655         [self removeAllToolTips];
   1656         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
   1657         _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
   1658         [self _sendToolTipMouseEntered];
   1659     }
   1660 }
   1661 
   1662 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
   1663 {
   1664     return [[_private->toolTip copy] autorelease];
   1665 }
   1666 
   1667 - (void)_updateMouseoverWithEvent:(NSEvent *)event
   1668 {
   1669     if (_private->closed)
   1670         return;
   1671 
   1672     NSView *contentView = [[event window] contentView];
   1673     NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
   1674 
   1675     forceWebHTMLViewHitTest = YES;
   1676     NSView *hitView = [contentView hitTest:locationForHitTest];
   1677     forceWebHTMLViewHitTest = NO;
   1678 
   1679     WebHTMLView *view = nil;
   1680     if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
   1681         view = (WebHTMLView *)hitView;
   1682 
   1683     if (view)
   1684         [view retain];
   1685 
   1686     if (lastHitView != view && lastHitView && [lastHitView _frame]) {
   1687         // If we are moving out of a view (or frame), let's pretend the mouse moved
   1688         // all the way out of that view. But we have to account for scrolling, because
   1689         // WebCore doesn't understand our clipping.
   1690         NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
   1691         float yScroll = visibleRect.origin.y;
   1692         float xScroll = visibleRect.origin.x;
   1693 
   1694         NSEvent *event = [NSEvent mouseEventWithType:NSMouseMoved
   1695             location:NSMakePoint(-1 - xScroll, -1 - yScroll)
   1696             modifierFlags:[[NSApp currentEvent] modifierFlags]
   1697             timestamp:[NSDate timeIntervalSinceReferenceDate]
   1698             windowNumber:[[view window] windowNumber]
   1699             context:[[NSApp currentEvent] context]
   1700             eventNumber:0 clickCount:0 pressure:0];
   1701         if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
   1702             lastHitCoreFrame->eventHandler()->mouseMoved(event);
   1703     }
   1704 
   1705     lastHitView = view;
   1706 
   1707     if (view) {
   1708         if (Frame* coreFrame = core([view _frame]))
   1709             coreFrame->eventHandler()->mouseMoved(event);
   1710 
   1711         [view release];
   1712     }
   1713 }
   1714 
   1715 + (NSArray *)_insertablePasteboardTypes
   1716 {
   1717     static NSArray *types = nil;
   1718     if (!types) {
   1719         types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType,
   1720 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
   1721             NSPICTPboardType,
   1722 #endif
   1723             NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
   1724         CFRetain(types);
   1725     }
   1726     return types;
   1727 }
   1728 
   1729 + (NSArray *)_selectionPasteboardTypes
   1730 {
   1731     // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
   1732     return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
   1733 }
   1734 
   1735 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
   1736 {
   1737     [self setPromisedDragTIFFDataSource:0];
   1738 }
   1739 
   1740 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
   1741 {
   1742     if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
   1743         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
   1744         [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
   1745         [archive release];
   1746     } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
   1747         if (Image* image = [self promisedDragTIFFDataSource]->image())
   1748             [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
   1749         [self setPromisedDragTIFFDataSource:0];
   1750     }
   1751 }
   1752 
   1753 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
   1754 {
   1755     [self autoscroll:event];
   1756     [self _startAutoscrollTimer:event];
   1757 }
   1758 
   1759 - (WebPluginController *)_pluginController
   1760 {
   1761     return _private->pluginController;
   1762 }
   1763 
   1764 - (void)_layoutForPrinting
   1765 {
   1766     // Set printing mode temporarily so we can adjust the size of the view. This will allow
   1767     // AppKit's pagination code to use the correct height for the page content. Leaving printing
   1768     // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
   1769     // turn it off again after adjusting the size.
   1770     [self _web_setPrintingModeRecursiveAndAdjustViewSize];
   1771     [self _web_clearPrintingModeRecursive];
   1772 }
   1773 
   1774 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
   1775 {
   1776     if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
   1777         if (beforeString)
   1778             *beforeString = nil;
   1779         if (afterString)
   1780             *afterString = nil;
   1781         return;
   1782     }
   1783 
   1784     [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
   1785 }
   1786 
   1787 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
   1788 {
   1789     return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
   1790 }
   1791 
   1792 - (void)_startAutoscrollTimer:(NSEvent *)triggerEvent
   1793 {
   1794     if (_private->autoscrollTimer == nil) {
   1795         _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
   1796             target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
   1797         _private->autoscrollTriggerEvent = [triggerEvent retain];
   1798     }
   1799 }
   1800 
   1801 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
   1802 // We can't remove this yet because it's still in use by Mail.
   1803 - (NSRect)_selectionRect
   1804 {
   1805     return [self selectionRect];
   1806 }
   1807 
   1808 - (void)_stopAutoscrollTimer
   1809 {
   1810     NSTimer *timer = _private->autoscrollTimer;
   1811     _private->autoscrollTimer = nil;
   1812     [_private->autoscrollTriggerEvent release];
   1813     _private->autoscrollTriggerEvent = nil;
   1814     [timer invalidate];
   1815     [timer release];
   1816 }
   1817 
   1818 - (void)_autoscroll
   1819 {
   1820     // Guarantee that the autoscroll timer is invalidated, even if we don't receive
   1821     // a mouse up event.
   1822     BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);
   1823     if (!isStillDown){
   1824         [self _stopAutoscrollTimer];
   1825         return;
   1826     }
   1827 
   1828     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
   1829         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
   1830         modifierFlags:[[NSApp currentEvent] modifierFlags]
   1831         timestamp:[NSDate timeIntervalSinceReferenceDate]
   1832         windowNumber:[[self window] windowNumber]
   1833         context:[[NSApp currentEvent] context]
   1834         eventNumber:0 clickCount:0 pressure:0];
   1835     [self mouseDragged:fakeEvent];
   1836 }
   1837 
   1838 - (BOOL)_canEdit
   1839 {
   1840     Frame* coreFrame = core([self _frame]);
   1841     return coreFrame && coreFrame->editor()->canEdit();
   1842 }
   1843 
   1844 - (BOOL)_canEditRichly
   1845 {
   1846     Frame* coreFrame = core([self _frame]);
   1847     return coreFrame && coreFrame->editor()->canEditRichly();
   1848 }
   1849 
   1850 - (BOOL)_canAlterCurrentSelection
   1851 {
   1852     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
   1853 }
   1854 
   1855 - (BOOL)_hasSelection
   1856 {
   1857     Frame* coreFrame = core([self _frame]);
   1858     return coreFrame && coreFrame->selection()->isRange();
   1859 }
   1860 
   1861 - (BOOL)_hasSelectionOrInsertionPoint
   1862 {
   1863     Frame* coreFrame = core([self _frame]);
   1864     return coreFrame && coreFrame->selection()->isCaretOrRange();
   1865 }
   1866 
   1867 - (BOOL)_hasInsertionPoint
   1868 {
   1869     Frame* coreFrame = core([self _frame]);
   1870     return coreFrame && coreFrame->selection()->isCaret();
   1871 }
   1872 
   1873 - (BOOL)_isEditable
   1874 {
   1875     Frame* coreFrame = core([self _frame]);
   1876     return coreFrame && coreFrame->selection()->isContentEditable();
   1877 }
   1878 
   1879 - (BOOL)_transparentBackground
   1880 {
   1881     return _private->transparentBackground;
   1882 }
   1883 
   1884 - (void)_setTransparentBackground:(BOOL)f
   1885 {
   1886     _private->transparentBackground = f;
   1887 }
   1888 
   1889 - (NSImage *)_selectionDraggingImage
   1890 {
   1891     if (![self _hasSelection])
   1892         return nil;
   1893     NSImage *dragImage = core([self _frame])->selectionImage();
   1894     [dragImage _web_dissolveToFraction:WebDragImageAlpha];
   1895     return dragImage;
   1896 }
   1897 
   1898 - (NSRect)_selectionDraggingRect
   1899 {
   1900     // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
   1901     return [self selectionRect];
   1902 }
   1903 
   1904 - (DOMNode *)_insertOrderedList
   1905 {
   1906     Frame* coreFrame = core([self _frame]);
   1907     return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
   1908 }
   1909 
   1910 - (DOMNode *)_insertUnorderedList
   1911 {
   1912     Frame* coreFrame = core([self _frame]);
   1913     return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
   1914 }
   1915 
   1916 - (BOOL)_canIncreaseSelectionListLevel
   1917 {
   1918     Frame* coreFrame = core([self _frame]);
   1919     return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
   1920 }
   1921 
   1922 - (BOOL)_canDecreaseSelectionListLevel
   1923 {
   1924     Frame* coreFrame = core([self _frame]);
   1925     return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
   1926 }
   1927 
   1928 - (DOMNode *)_increaseSelectionListLevel
   1929 {
   1930     Frame* coreFrame = core([self _frame]);
   1931     return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
   1932 }
   1933 
   1934 - (DOMNode *)_increaseSelectionListLevelOrdered
   1935 {
   1936     Frame* coreFrame = core([self _frame]);
   1937     return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
   1938 }
   1939 
   1940 - (DOMNode *)_increaseSelectionListLevelUnordered
   1941 {
   1942     Frame* coreFrame = core([self _frame]);
   1943     return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
   1944 }
   1945 
   1946 - (void)_decreaseSelectionListLevel
   1947 {
   1948     Frame* coreFrame = core([self _frame]);
   1949     if (coreFrame)
   1950         coreFrame->editor()->decreaseSelectionListLevel();
   1951 }
   1952 
   1953 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
   1954 {
   1955     if (!_private->highlighters)
   1956         _private->highlighters = [[NSMutableDictionary alloc] init];
   1957     [_private->highlighters setObject:highlighter forKey:type];
   1958 }
   1959 
   1960 - (void)_removeHighlighterOfType:(NSString*)type
   1961 {
   1962     [_private->highlighters removeObjectForKey:type];
   1963 }
   1964 
   1965 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
   1966 {
   1967     ASSERT([self _hasSelection]);
   1968     NSArray *types = [self pasteboardTypesForSelection];
   1969 
   1970     // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
   1971     NSAttributedString *attributedString = [self selectedAttributedString];
   1972     NSMutableArray *mutableTypes = nil;
   1973     if (![attributedString containsAttachments]) {
   1974         mutableTypes = [types mutableCopy];
   1975         [mutableTypes removeObject:NSRTFDPboardType];
   1976         types = mutableTypes;
   1977     }
   1978 
   1979     [pasteboard declareTypes:types owner:[self _topHTMLView]];
   1980     [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
   1981     [mutableTypes release];
   1982 }
   1983 
   1984 - (void)close
   1985 {
   1986     // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing
   1987     // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
   1988     if (!_private || _private->closed)
   1989         return;
   1990 
   1991     _private->closed = YES;
   1992 
   1993     [self _cancelUpdateMouseoverTimer];
   1994     [self _clearLastHitViewIfSelf];
   1995     [self _removeMouseMovedObserverUnconditionally];
   1996     [self _removeWindowObservers];
   1997     [self _removeSuperviewObservers];
   1998     [_private->pluginController destroyAllPlugins];
   1999     [_private->pluginController setDataSource:nil];
   2000     // remove tooltips before clearing _private so removeTrackingRect: will work correctly
   2001     [self removeAllToolTips];
   2002 
   2003     if (_private->isInSecureInputState) {
   2004         DisableSecureEventInput();
   2005         _private->isInSecureInputState = NO;
   2006     }
   2007 
   2008     [_private clear];
   2009 }
   2010 
   2011 - (BOOL)_hasHTMLDocument
   2012 {
   2013     Frame* coreFrame = core([self _frame]);
   2014     if (!coreFrame)
   2015         return NO;
   2016     Document* document = coreFrame->document();
   2017     return document && document->isHTMLDocument();
   2018 }
   2019 
   2020 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
   2021                                                  forType:(NSString *)pboardType
   2022                                                inContext:(DOMRange *)context
   2023                                             subresources:(NSArray **)subresources
   2024 {
   2025     if (pboardType == WebArchivePboardType) {
   2026         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
   2027         if (subresources)
   2028             *subresources = [archive subresources];
   2029         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
   2030         [archive release];
   2031         return fragment;
   2032     }
   2033     if (pboardType == NSFilenamesPboardType)
   2034         return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
   2035 
   2036     if (pboardType == NSHTMLPboardType) {
   2037         NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
   2038         // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
   2039         if ([HTMLString hasPrefix:@"Version:"]) {
   2040             NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
   2041             if (range.location != NSNotFound)
   2042                 HTMLString = [HTMLString substringFromIndex:range.location];
   2043         }
   2044         if ([HTMLString length] == 0)
   2045             return nil;
   2046 
   2047         return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
   2048     }
   2049 
   2050     // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
   2051     // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
   2052     // FIXME: Remove this once bug 5052369 is fixed.
   2053     if ([self _hasHTMLDocument] && (pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType)) {
   2054         NSAttributedString *string = nil;
   2055         if (pboardType == NSRTFDPboardType)
   2056             string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
   2057         if (string == nil)
   2058             string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
   2059         if (string == nil)
   2060             return nil;
   2061 
   2062         NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
   2063             [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
   2064             self, @"WebResourceHandler", nil];
   2065         NSArray *s;
   2066 
   2067         BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
   2068         if (!wasDeferringCallbacks)
   2069             [[self _webView] setDefersCallbacks:YES];
   2070 
   2071         DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length])
   2072                                                           document:[[self _frame] DOMDocument]
   2073                                                 documentAttributes:documentAttributes
   2074                                                       subresources:&s];
   2075         if (subresources)
   2076             *subresources = s;
   2077 
   2078         NSEnumerator *e = [s objectEnumerator];
   2079         WebResource *r;
   2080         while ((r = [e nextObject]))
   2081             [[self _dataSource] addSubresource:r];
   2082 
   2083         if (!wasDeferringCallbacks)
   2084             [[self _webView] setDefersCallbacks:NO];
   2085 
   2086         [documentAttributes release];
   2087         [string release];
   2088         return fragment;
   2089     }
   2090     if (pboardType == NSTIFFPboardType) {
   2091         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
   2092                                                               URL:uniqueURLWithRelativePart(@"image.tiff")
   2093                                                          MIMEType:@"image/tiff"
   2094                                                  textEncodingName:nil
   2095                                                         frameName:nil];
   2096         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
   2097         [resource release];
   2098         return fragment;
   2099     }
   2100     if (pboardType == NSPDFPboardType) {
   2101         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPDFPboardType]
   2102                                                               URL:uniqueURLWithRelativePart(@"application.pdf")
   2103                                                          MIMEType:@"application/pdf"
   2104                                                  textEncodingName:nil
   2105                                                         frameName:nil];
   2106         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
   2107         [resource release];
   2108         return fragment;
   2109     }
   2110 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
   2111     if (pboardType == NSPICTPboardType) {
   2112         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
   2113                                                               URL:uniqueURLWithRelativePart(@"image.pict")
   2114                                                          MIMEType:@"image/pict"
   2115                                                  textEncodingName:nil
   2116                                                         frameName:nil];
   2117         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
   2118         [resource release];
   2119         return fragment;
   2120     }
   2121 #endif
   2122     // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
   2123     // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
   2124     if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) {
   2125         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG]
   2126                                                               URL:uniqueURLWithRelativePart(@"image.png")
   2127                                                          MIMEType:@"image/png"
   2128                                                  textEncodingName:nil
   2129                                                         frameName:nil];
   2130         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
   2131         [resource release];
   2132         return fragment;
   2133     }
   2134     if (pboardType == NSURLPboardType) {
   2135         NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
   2136         DOMDocument* document = [[self _frame] DOMDocument];
   2137         ASSERT(document);
   2138         if (!document)
   2139             return nil;
   2140         DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
   2141         NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose.
   2142         if ([URLString length] == 0)
   2143             return nil;
   2144         NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping];
   2145         DOMText *text = [document createTextNode:URLTitleString];
   2146         [anchor setHref:URLString];
   2147         [anchor appendChild:text];
   2148         DOMDocumentFragment *fragment = [document createDocumentFragment];
   2149         [fragment appendChild:anchor];
   2150         return fragment;
   2151     }
   2152     if (pboardType == NSStringPboardType)
   2153         return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get());
   2154     return nil;
   2155 }
   2156 
   2157 #if ENABLE(NETSCAPE_PLUGIN_API)
   2158 - (void)_pauseNullEventsForAllNetscapePlugins
   2159 {
   2160     NSArray *subviews = [self subviews];
   2161     unsigned int subviewCount = [subviews count];
   2162     unsigned int subviewIndex;
   2163 
   2164     for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
   2165         NSView *subview = [subviews objectAtIndex:subviewIndex];
   2166         if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
   2167             [(WebBaseNetscapePluginView *)subview stopTimers];
   2168     }
   2169 }
   2170 #endif
   2171 
   2172 #if ENABLE(NETSCAPE_PLUGIN_API)
   2173 - (void)_resumeNullEventsForAllNetscapePlugins
   2174 {
   2175     NSArray *subviews = [self subviews];
   2176     unsigned int subviewCount = [subviews count];
   2177     unsigned int subviewIndex;
   2178 
   2179     for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
   2180         NSView *subview = [subviews objectAtIndex:subviewIndex];
   2181         if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
   2182             [(WebBaseNetscapePluginView *)subview restartTimers];
   2183     }
   2184 }
   2185 #endif
   2186 
   2187 - (BOOL)_isUsingAcceleratedCompositing
   2188 {
   2189 #if USE(ACCELERATED_COMPOSITING)
   2190     return _private->layerHostingView != nil;
   2191 #else
   2192     return NO;
   2193 #endif
   2194 }
   2195 
   2196 - (NSView *)_compositingLayersHostingView
   2197 {
   2198 #if USE(ACCELERATED_COMPOSITING)
   2199     return _private->layerHostingView;
   2200 #else
   2201     return 0;
   2202 #endif
   2203 }
   2204 
   2205 - (BOOL)_isInPrintMode
   2206 {
   2207     return _private->printing;
   2208 }
   2209 
   2210 - (BOOL)_beginPrintModeWithMinimumPageWidth:(CGFloat)minimumPageWidth height:(CGFloat)minimumPageHeight maximumPageWidth:(CGFloat)maximumPageWidth
   2211 {
   2212     Frame* frame = core([self _frame]);
   2213     if (!frame)
   2214         return NO;
   2215 
   2216     if (frame->document() && frame->document()->isFrameSet()) {
   2217         minimumPageWidth = 0;
   2218         minimumPageHeight = 0;
   2219         maximumPageWidth = 0;
   2220     }
   2221 
   2222     [self _setPrinting:YES minimumPageLogicalWidth:minimumPageWidth logicalHeight:minimumPageHeight maximumPageLogicalWidth:maximumPageWidth adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
   2223     return YES;
   2224 }
   2225 
   2226 - (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth height:(float)pageHeight shrinkToFit:(BOOL)shrinkToFit
   2227 {
   2228     Frame* frame = core([self _frame]);
   2229     if (!frame)
   2230         return NO;
   2231 
   2232     Document* document = frame->document();
   2233     bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
   2234 
   2235     float minLayoutLogicalWidth = isHorizontal ? pageWidth : pageHeight;
   2236     float minLayoutLogicalHeight = isHorizontal ? pageHeight : pageWidth;
   2237     float maxLayoutLogicalWidth = minLayoutLogicalWidth;
   2238 
   2239     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
   2240     // according to the page width.
   2241     if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
   2242         minLayoutLogicalWidth *= _WebHTMLViewPrintingMinimumShrinkFactor;
   2243         minLayoutLogicalHeight *= _WebHTMLViewPrintingMinimumShrinkFactor;
   2244         maxLayoutLogicalWidth *= _WebHTMLViewPrintingMaximumShrinkFactor;
   2245     }
   2246     [self _setPrinting:YES minimumPageLogicalWidth:minLayoutLogicalWidth logicalHeight:minLayoutLogicalHeight maximumPageLogicalWidth:maxLayoutLogicalWidth adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
   2247 
   2248     return YES;
   2249 }
   2250 
   2251 - (void)_endPrintMode
   2252 {
   2253     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
   2254 }
   2255 
   2256 - (BOOL)_isInScreenPaginationMode
   2257 {
   2258     return _private->paginateScreenContent;
   2259 }
   2260 
   2261 - (BOOL)_beginScreenPaginationModeWithPageSize:(CGSize)pageSize shrinkToFit:(BOOL)shrinkToFit
   2262 {
   2263     Frame* frame = core([self _frame]);
   2264     if (!frame)
   2265         return NO;
   2266 
   2267     Document* document = frame->document();
   2268     bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
   2269 
   2270     float minLayoutLogicalWidth = isHorizontal ? pageSize.width : pageSize.height;
   2271     float minLayoutLogicalHeight = isHorizontal ? pageSize.height : pageSize.width;
   2272     float maxLayoutLogicalWidth = minLayoutLogicalWidth;
   2273 
   2274     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
   2275     // according to the page width.
   2276     if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
   2277         minLayoutLogicalWidth *= _WebHTMLViewPrintingMinimumShrinkFactor;
   2278         minLayoutLogicalHeight *= _WebHTMLViewPrintingMinimumShrinkFactor;
   2279         maxLayoutLogicalWidth *= _WebHTMLViewPrintingMaximumShrinkFactor;
   2280     }
   2281     [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:minLayoutLogicalWidth logicalHeight:minLayoutLogicalHeight maximumPageLogicalWidth:maxLayoutLogicalWidth adjustViewSize:YES paginateScreenContent:YES];
   2282 
   2283     return YES;
   2284 }
   2285 
   2286 - (void)_endScreenPaginationMode
   2287 {
   2288     [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:YES paginateScreenContent:NO];
   2289 }
   2290 
   2291 - (CGFloat)_adjustedBottomOfPageWithTop:(CGFloat)top bottom:(CGFloat)bottom limit:(CGFloat)bottomLimit
   2292 {
   2293     Frame* frame = core([self _frame]);
   2294     if (!frame)
   2295         return bottom;
   2296 
   2297     FrameView* view = frame->view();
   2298     if (!view)
   2299         return bottom;
   2300 
   2301     float newBottom;
   2302     view->adjustPageHeightDeprecated(&newBottom, top, bottom, bottomLimit);
   2303 
   2304 #ifdef __LP64__
   2305     // If the new bottom is equal to the old bottom (when both are treated as floats), we just return the original
   2306     // bottom. This prevents rounding errors that can occur when converting newBottom to a double.
   2307     if (fabs(static_cast<float>(bottom) - newBottom) <= numeric_limits<float>::epsilon())
   2308         return bottom;
   2309     else
   2310 #endif
   2311         return newBottom;
   2312 }
   2313 
   2314 @end
   2315 
   2316 @implementation NSView (WebHTMLViewFileInternal)
   2317 
   2318 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
   2319 {
   2320     unsigned count = [_subviews count];
   2321     for (unsigned i = 0; i < count; ++i) {
   2322         NSView *child = [_subviews objectAtIndex:i];
   2323         if ([child isKindOfClass:[WebHTMLView class]])
   2324             [array addObject:child];
   2325         [child _web_addDescendantWebHTMLViewsToArray:array];
   2326     }
   2327 }
   2328 
   2329 @end
   2330 
   2331 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
   2332 
   2333 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
   2334 {
   2335     if (object == nil) {
   2336         [self removeObjectForKey:key];
   2337     } else {
   2338         [self setObject:object forKey:key];
   2339     }
   2340 }
   2341 
   2342 @end
   2343 
   2344 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
   2345 {
   2346     NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
   2347     return [filename _webkit_hasCaseInsensitiveSuffix:extensionAsSuffix]
   2348         || ([extension _webkit_isCaseInsensitiveEqualToString:@"jpeg"]
   2349             && [filename _webkit_hasCaseInsensitiveSuffix:@".jpg"]);
   2350 }
   2351 
   2352 #ifdef BUILDING_ON_TIGER
   2353 
   2354 // The following is a workaround for
   2355 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
   2356 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
   2357 // Since the category will be searched before the real class, we'll prevent the flag from being
   2358 // set on the tool tip panel.
   2359 
   2360 @interface NSToolTipPanel : NSPanel
   2361 @end
   2362 
   2363 @interface NSToolTipPanel (WebHTMLViewFileInternal)
   2364 @end
   2365 
   2366 @implementation NSToolTipPanel (WebHTMLViewFileInternal)
   2367 
   2368 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
   2369 {
   2370     // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
   2371 }
   2372 
   2373 @end
   2374 
   2375 #endif
   2376 
   2377 @implementation WebHTMLView
   2378 
   2379 + (void)initialize
   2380 {
   2381     [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes]
   2382                              returnTypes:[[self class] _insertablePasteboardTypes]];
   2383     JSC::initializeThreading();
   2384     WTF::initializeMainThreadToProcessMainThread();
   2385 #ifndef BUILDING_ON_TIGER
   2386     WebCoreObjCFinalizeOnMainThread(self);
   2387 #endif
   2388 }
   2389 
   2390 - (id)initWithFrame:(NSRect)frame
   2391 {
   2392     self = [super initWithFrame:frame];
   2393     if (!self)
   2394         return nil;
   2395 
   2396     [self setFocusRingType:NSFocusRingTypeNone];
   2397 
   2398     // Make all drawing go through us instead of subviews.
   2399     [self _setDrawsOwnDescendants:YES];
   2400 
   2401     _private = [[WebHTMLViewPrivate alloc] init];
   2402 
   2403     _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
   2404 
   2405     return self;
   2406 }
   2407 
   2408 - (void)dealloc
   2409 {
   2410     if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self))
   2411         return;
   2412 
   2413     // We can't assert that close has already been called because
   2414     // this view can be removed from it's superview, even though
   2415     // it could be needed later, so close if needed.
   2416     [self close];
   2417     [_private release];
   2418     _private = nil;
   2419     [super dealloc];
   2420 }
   2421 
   2422 - (void)finalize
   2423 {
   2424     ASSERT_MAIN_THREAD();
   2425     // We can't assert that close has already been called because
   2426     // this view can be removed from it's superview, even though
   2427     // it could be needed later, so close if needed.
   2428     [self close];
   2429     [super finalize];
   2430 }
   2431 
   2432 // Returns YES if the delegate returns YES (so we should do no more work).
   2433 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
   2434 {
   2435     BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
   2436     _private->selectorForDoCommandBySelector = 0;
   2437     if (callerAlreadyCalledDelegate)
   2438         return NO;
   2439     WebView *webView = [self _webView];
   2440     return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
   2441 }
   2442 
   2443 typedef HashMap<SEL, String> SelectorNameMap;
   2444 
   2445 // Map selectors into Editor command names.
   2446 // This is not needed for any selectors that have the same name as the Editor command.
   2447 static const SelectorNameMap* createSelectorExceptionMap()
   2448 {
   2449     SelectorNameMap* map = new HashMap<SEL, String>;
   2450 
   2451     map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline");
   2452     map->add(@selector(insertParagraphSeparator:), "InsertNewline");
   2453     map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab");
   2454     map->add(@selector(pageDown:), "MovePageDown");
   2455     map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection");
   2456     map->add(@selector(pageUp:), "MovePageUp");
   2457     map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection");
   2458 
   2459     return map;
   2460 }
   2461 
   2462 static String commandNameForSelector(SEL selector)
   2463 {
   2464     // Check the exception map first.
   2465     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
   2466     SelectorNameMap::const_iterator it = exceptionMap->find(selector);
   2467     if (it != exceptionMap->end())
   2468         return it->second;
   2469 
   2470     // Remove the trailing colon.
   2471     // No need to capitalize the command name since Editor command names are
   2472     // not case sensitive.
   2473     const char* selectorName = sel_getName(selector);
   2474     size_t selectorNameLength = strlen(selectorName);
   2475     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
   2476         return String();
   2477     return String(selectorName, selectorNameLength - 1);
   2478 }
   2479 
   2480 - (Editor::Command)coreCommandBySelector:(SEL)selector
   2481 {
   2482     Frame* coreFrame = core([self _frame]);
   2483     if (!coreFrame)
   2484         return Editor::Command();
   2485     return coreFrame->editor()->command(commandNameForSelector(selector));
   2486 }
   2487 
   2488 - (Editor::Command)coreCommandByName:(const char*)name
   2489 {
   2490     Frame* coreFrame = core([self _frame]);
   2491     if (!coreFrame)
   2492         return Editor::Command();
   2493     return coreFrame->editor()->command(name);
   2494 }
   2495 
   2496 - (void)executeCoreCommandBySelector:(SEL)selector
   2497 {
   2498     if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
   2499         return;
   2500     [self coreCommandBySelector:selector].execute();
   2501 }
   2502 
   2503 - (void)executeCoreCommandByName:(const char*)name
   2504 {
   2505     [self coreCommandByName:name].execute();
   2506 }
   2507 
   2508 // These commands are forwarded to the Editor object in WebCore.
   2509 // Ideally we'd do this for all editing commands; more of the code
   2510 // should be moved from here to there, and more commands should be
   2511 // added to this list.
   2512 
   2513 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
   2514 // The functions are identical.
   2515 
   2516 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
   2517 
   2518 WEBCORE_COMMAND(alignCenter)
   2519 WEBCORE_COMMAND(alignJustified)
   2520 WEBCORE_COMMAND(alignLeft)
   2521 WEBCORE_COMMAND(alignRight)
   2522 WEBCORE_COMMAND(copy)
   2523 WEBCORE_COMMAND(cut)
   2524 WEBCORE_COMMAND(paste)
   2525 WEBCORE_COMMAND(delete)
   2526 WEBCORE_COMMAND(deleteBackward)
   2527 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
   2528 WEBCORE_COMMAND(deleteForward)
   2529 WEBCORE_COMMAND(deleteToBeginningOfLine)
   2530 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
   2531 WEBCORE_COMMAND(deleteToEndOfLine)
   2532 WEBCORE_COMMAND(deleteToEndOfParagraph)
   2533 WEBCORE_COMMAND(deleteToMark)
   2534 WEBCORE_COMMAND(deleteWordBackward)
   2535 WEBCORE_COMMAND(deleteWordForward)
   2536 WEBCORE_COMMAND(ignoreSpelling)
   2537 WEBCORE_COMMAND(indent)
   2538 WEBCORE_COMMAND(insertBacktab)
   2539 WEBCORE_COMMAND(insertLineBreak)
   2540 WEBCORE_COMMAND(insertNewline)
   2541 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
   2542 WEBCORE_COMMAND(insertParagraphSeparator)
   2543 WEBCORE_COMMAND(insertTab)
   2544 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
   2545 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
   2546 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
   2547 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
   2548 WEBCORE_COMMAND(moveBackward)
   2549 WEBCORE_COMMAND(moveBackwardAndModifySelection)
   2550 WEBCORE_COMMAND(moveDown)
   2551 WEBCORE_COMMAND(moveDownAndModifySelection)
   2552 WEBCORE_COMMAND(moveForward)
   2553 WEBCORE_COMMAND(moveForwardAndModifySelection)
   2554 WEBCORE_COMMAND(moveLeft)
   2555 WEBCORE_COMMAND(moveLeftAndModifySelection)
   2556 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
   2557 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
   2558 WEBCORE_COMMAND(moveRight)
   2559 WEBCORE_COMMAND(moveRightAndModifySelection)
   2560 WEBCORE_COMMAND(moveToBeginningOfDocument)
   2561 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
   2562 WEBCORE_COMMAND(moveToBeginningOfLine)
   2563 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
   2564 WEBCORE_COMMAND(moveToBeginningOfParagraph)
   2565 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
   2566 WEBCORE_COMMAND(moveToBeginningOfSentence)
   2567 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
   2568 WEBCORE_COMMAND(moveToEndOfDocument)
   2569 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
   2570 WEBCORE_COMMAND(moveToEndOfLine)
   2571 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
   2572 WEBCORE_COMMAND(moveToEndOfParagraph)
   2573 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
   2574 WEBCORE_COMMAND(moveToEndOfSentence)
   2575 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
   2576 WEBCORE_COMMAND(moveToLeftEndOfLine)
   2577 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
   2578 WEBCORE_COMMAND(moveToRightEndOfLine)
   2579 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
   2580 WEBCORE_COMMAND(moveUp)
   2581 WEBCORE_COMMAND(moveUpAndModifySelection)
   2582 WEBCORE_COMMAND(moveWordBackward)
   2583 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
   2584 WEBCORE_COMMAND(moveWordForward)
   2585 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
   2586 WEBCORE_COMMAND(moveWordLeft)
   2587 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
   2588 WEBCORE_COMMAND(moveWordRight)
   2589 WEBCORE_COMMAND(moveWordRightAndModifySelection)
   2590 WEBCORE_COMMAND(outdent)
   2591 WEBCORE_COMMAND(pageDown)
   2592 WEBCORE_COMMAND(pageDownAndModifySelection)
   2593 WEBCORE_COMMAND(pageUp)
   2594 WEBCORE_COMMAND(pageUpAndModifySelection)
   2595 WEBCORE_COMMAND(pasteAsPlainText)
   2596 WEBCORE_COMMAND(selectAll)
   2597 WEBCORE_COMMAND(selectLine)
   2598 WEBCORE_COMMAND(selectParagraph)
   2599 WEBCORE_COMMAND(selectSentence)
   2600 WEBCORE_COMMAND(selectToMark)
   2601 WEBCORE_COMMAND(selectWord)
   2602 WEBCORE_COMMAND(setMark)
   2603 WEBCORE_COMMAND(subscript)
   2604 WEBCORE_COMMAND(superscript)
   2605 WEBCORE_COMMAND(swapWithMark)
   2606 WEBCORE_COMMAND(transpose)
   2607 WEBCORE_COMMAND(underline)
   2608 WEBCORE_COMMAND(unscript)
   2609 WEBCORE_COMMAND(yank)
   2610 WEBCORE_COMMAND(yankAndSelect)
   2611 
   2612 #undef WEBCORE_COMMAND
   2613 
   2614 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
   2615 
   2616 - (IBAction)takeFindStringFromSelection:(id)sender
   2617 {
   2618     COMMAND_PROLOGUE
   2619 
   2620     if (![self _hasSelection]) {
   2621         NSBeep();
   2622         return;
   2623     }
   2624 
   2625     [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
   2626 }
   2627 
   2628 // This method is needed to support Mac OS X services.
   2629 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
   2630 {
   2631     [pasteboard declareTypes:types owner:[self _topHTMLView]];
   2632     [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
   2633     return YES;
   2634 }
   2635 
   2636 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
   2637 {
   2638     BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]);
   2639     BOOL isReturnTypeOK = NO;
   2640     if (!returnType)
   2641         isReturnTypeOK = YES;
   2642     else if ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
   2643         // We can insert strings in any editable context.  We can insert other types, like images, only in rich edit contexts.
   2644         isReturnTypeOK = [returnType isEqualToString:NSStringPboardType] || [self _canEditRichly];
   2645     }
   2646     if (isSendTypeOK && isReturnTypeOK)
   2647         return self;
   2648     return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
   2649 }
   2650 
   2651 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
   2652 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
   2653 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
   2654 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
   2655 // might be using the selector, and we don't want to break them.
   2656 - (void)jumpToSelection:(id)sender
   2657 {
   2658     COMMAND_PROLOGUE
   2659 
   2660     if (Frame* coreFrame = core([self _frame]))
   2661         coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways);
   2662 }
   2663 
   2664 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
   2665 {
   2666     SEL action = [item action];
   2667     RefPtr<Frame> frame = core([self _frame]);
   2668 
   2669     if (!frame)
   2670         return NO;
   2671 
   2672     if (Document* doc = frame->document()) {
   2673         if (doc->isPluginDocument())
   2674             return NO;
   2675         if (doc->isImageDocument()) {
   2676             if (action == @selector(copy:))
   2677                 return frame->loader()->isComplete();
   2678             return NO;
   2679         }
   2680     }
   2681 
   2682     if (action == @selector(changeSpelling:)
   2683             || action == @selector(_changeSpellingFromMenu:)
   2684             || action == @selector(checkSpelling:)
   2685             || action == @selector(complete:)
   2686             || action == @selector(pasteFont:))
   2687         return [self _canEdit];
   2688 
   2689     if (action == @selector(showGuessPanel:)) {
   2690 #ifndef BUILDING_ON_TIGER
   2691         // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
   2692         NSMenuItem *menuItem = (NSMenuItem *)item;
   2693         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
   2694             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
   2695             [menuItem setTitle:panelShowing
   2696                 ? UI_STRING_INTERNAL("Hide Spelling and Grammar", "menu item title")
   2697                 : UI_STRING_INTERNAL("Show Spelling and Grammar", "menu item title")];
   2698         }
   2699 #endif
   2700         return [self _canEdit];
   2701     }
   2702 
   2703     if (action == @selector(changeBaseWritingDirection:)
   2704             || action == @selector(makeBaseWritingDirectionLeftToRight:)
   2705             || action == @selector(makeBaseWritingDirectionRightToLeft:)) {
   2706         NSWritingDirection writingDirection;
   2707 
   2708         if (action == @selector(changeBaseWritingDirection:)) {
   2709             writingDirection = static_cast<NSWritingDirection>([item tag]);
   2710             if (writingDirection == NSWritingDirectionNatural)
   2711                 return NO;
   2712         } else if (action == @selector(makeBaseWritingDirectionLeftToRight:))
   2713             writingDirection = NSWritingDirectionLeftToRight;
   2714         else
   2715             writingDirection = NSWritingDirectionRightToLeft;
   2716 
   2717         NSMenuItem *menuItem = (NSMenuItem *)item;
   2718         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
   2719             String direction = writingDirection == NSWritingDirectionLeftToRight ? "ltr" : "rtl";
   2720             [menuItem setState:frame->editor()->selectionHasStyle(CSSPropertyDirection, direction)];
   2721         }
   2722         return [self _canEdit];
   2723     }
   2724 
   2725     if (action == @selector(makeBaseWritingDirectionNatural:)) {
   2726         NSMenuItem *menuItem = (NSMenuItem *)item;
   2727         if ([menuItem isKindOfClass:[NSMenuItem class]])
   2728             [menuItem setState:NSOffState];
   2729         return NO;
   2730     }
   2731 
   2732     if (action == @selector(toggleBaseWritingDirection:)) {
   2733         NSMenuItem *menuItem = (NSMenuItem *)item;
   2734         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
   2735             // Take control of the title of the menu item instead of just checking/unchecking it because
   2736             // a check would be ambiguous.
   2737             [menuItem setTitle:frame->editor()->selectionHasStyle(CSSPropertyDirection, "rtl")
   2738                 ? UI_STRING_INTERNAL("Left to Right", "Left to Right context menu item")
   2739                 : UI_STRING_INTERNAL("Right to Left", "Right to Left context menu item")];
   2740         }
   2741         return [self _canEdit];
   2742     }
   2743 
   2744     if (action == @selector(changeAttributes:)
   2745             || action == @selector(changeColor:)
   2746             || action == @selector(changeFont:))
   2747         return [self _canEditRichly];
   2748 
   2749     if (action == @selector(capitalizeWord:)
   2750                || action == @selector(lowercaseWord:)
   2751                || action == @selector(uppercaseWord:))
   2752         return [self _hasSelection] && [self _isEditable];
   2753 
   2754     if (action == @selector(centerSelectionInVisibleArea:)
   2755                || action == @selector(jumpToSelection:)
   2756                || action == @selector(copyFont:))
   2757         return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
   2758 
   2759     if (action == @selector(changeDocumentBackgroundColor:))
   2760         return [[self _webView] isEditable] && [self _canEditRichly];
   2761 
   2762     if (action == @selector(_ignoreSpellingFromMenu:)
   2763             || action == @selector(_learnSpellingFromMenu:)
   2764             || action == @selector(takeFindStringFromSelection:))
   2765         return [self _hasSelection];
   2766 
   2767     if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
   2768         return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste());
   2769 
   2770     if (action == @selector(pasteAsRichText:))
   2771         return frame && (frame->editor()->canDHTMLPaste()
   2772             || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable()));
   2773 
   2774     if (action == @selector(performFindPanelAction:))
   2775         return NO;
   2776 
   2777     if (action == @selector(_lookUpInDictionaryFromMenu:))
   2778         return [self _hasSelection];
   2779 
   2780     if (action == @selector(stopSpeaking:))
   2781         return [NSApp isSpeaking];
   2782 
   2783 #ifndef BUILDING_ON_TIGER
   2784     if (action == @selector(toggleGrammarChecking:)) {
   2785         // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate
   2786         // the selector here because we implement it here, and we must implement it here because the AppKit
   2787         // code checks the first responder.
   2788         NSMenuItem *menuItem = (NSMenuItem *)item;
   2789         if ([menuItem isKindOfClass:[NSMenuItem class]])
   2790             [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
   2791         return YES;
   2792     }
   2793 #endif
   2794 
   2795 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
   2796     if (action == @selector(orderFrontSubstitutionsPanel:)) {
   2797         NSMenuItem *menuItem = (NSMenuItem *)item;
   2798         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
   2799             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
   2800             [menuItem setTitle:panelShowing
   2801                 ? UI_STRING_INTERNAL("Hide Substitutions", "menu item title")
   2802                 : UI_STRING_INTERNAL("Show Substitutions", "menu item title")];
   2803         }
   2804         return [self _canEdit];
   2805     }
   2806     // FIXME 4799134: WebView is the bottleneck for this logic, but we must validate
   2807     // the selector here because we implement it here, and we must implement it here because the AppKit
   2808     // code checks the first responder.
   2809     if (action == @selector(toggleSmartInsertDelete:)) {
   2810         NSMenuItem *menuItem = (NSMenuItem *)item;
   2811         if ([menuItem isKindOfClass:[NSMenuItem class]])
   2812             [menuItem setState:[self smartInsertDeleteEnabled] ? NSOnState : NSOffState];
   2813         return [self _canEdit];
   2814     }
   2815     if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
   2816         NSMenuItem *menuItem = (NSMenuItem *)item;
   2817         if ([menuItem isKindOfClass:[NSMenuItem class]])
   2818             [menuItem setState:[self isAutomaticQuoteSubstitutionEnabled] ? NSOnState : NSOffState];
   2819         return [self _canEdit];
   2820     }
   2821     if (action == @selector(toggleAutomaticLinkDetection:)) {
   2822         NSMenuItem *menuItem = (NSMenuItem *)item;
   2823         if ([menuItem isKindOfClass:[NSMenuItem class]])
   2824             [menuItem setState:[self isAutomaticLinkDetectionEnabled] ? NSOnState : NSOffState];
   2825         return [self _canEdit];
   2826     }
   2827     if (action == @selector(toggleAutomaticDashSubstitution:)) {
   2828         NSMenuItem *menuItem = (NSMenuItem *)item;
   2829         if ([menuItem isKindOfClass:[NSMenuItem class]])
   2830             [menuItem setState:[self isAutomaticDashSubstitutionEnabled] ? NSOnState : NSOffState];
   2831         return [self _canEdit];
   2832     }
   2833     if (action == @selector(toggleAutomaticTextReplacement:)) {
   2834         NSMenuItem *menuItem = (NSMenuItem *)item;
   2835         if ([menuItem isKindOfClass:[NSMenuItem class]])
   2836             [menuItem setState:[self isAutomaticTextReplacementEnabled] ? NSOnState : NSOffState];
   2837         return [self _canEdit];
   2838     }
   2839     if (action == @selector(toggleAutomaticSpellingCorrection:)) {
   2840         NSMenuItem *menuItem = (NSMenuItem *)item;
   2841         if ([menuItem isKindOfClass:[NSMenuItem class]])
   2842             [menuItem setState:[self isAutomaticSpellingCorrectionEnabled] ? NSOnState : NSOffState];
   2843         return [self _canEdit];
   2844     }
   2845 #endif
   2846 
   2847     Editor::Command command = [self coreCommandBySelector:action];
   2848     if (command.isSupported()) {
   2849         NSMenuItem *menuItem = (NSMenuItem *)item;
   2850         if ([menuItem isKindOfClass:[NSMenuItem class]])
   2851             [menuItem setState:kit(command.state())];
   2852         return command.isEnabled();
   2853     }
   2854 
   2855     return YES;
   2856 }
   2857 
   2858 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
   2859 {
   2860     // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
   2861     // assumes the WebVIew is non-nil.
   2862     if (![self _webView])
   2863         return NO;
   2864     BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
   2865     return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
   2866 }
   2867 
   2868 - (BOOL)acceptsFirstResponder
   2869 {
   2870     // Don't accept first responder when we first click on this view.
   2871     // We have to pass the event down through WebCore first to be sure we don't hit a subview.
   2872     // Do accept first responder at any other time, for example from keyboard events,
   2873     // or from calls back from WebCore once we begin mouse-down event handling.
   2874     NSEvent *event = [NSApp currentEvent];
   2875     if ([event type] == NSLeftMouseDown
   2876             && !_private->handlingMouseDownEvent
   2877             && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
   2878         return NO;
   2879     }
   2880     return YES;
   2881 }
   2882 
   2883 - (BOOL)maintainsInactiveSelection
   2884 {
   2885     // This method helps to determine whether the WebHTMLView should maintain
   2886     // an inactive selection when it's not first responder.
   2887     // Traditionally, these views have not maintained such selections,
   2888     // clearing them when the view was not first responder. However,
   2889     // to fix bugs like this one:
   2890     // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even
   2891     //                            when they're not firstResponder"
   2892     // it was decided to add a switch to act more like an NSTextView.
   2893 
   2894     if ([[self _webView] maintainsInactiveSelection])
   2895         return YES;
   2896 
   2897     // Predict the case where we are losing first responder status only to
   2898     // gain it back again. Want to keep the selection in that case.
   2899     id nextResponder = [[self window] _newFirstResponderAfterResigning];
   2900     if ([nextResponder isKindOfClass:[NSScrollView class]]) {
   2901         id contentView = [nextResponder contentView];
   2902         if (contentView)
   2903             nextResponder = contentView;
   2904     }
   2905     if ([nextResponder isKindOfClass:[NSClipView class]]) {
   2906         id documentView = [nextResponder documentView];
   2907         if (documentView)
   2908             nextResponder = documentView;
   2909     }
   2910     if (nextResponder == self)
   2911         return YES;
   2912 
   2913     Frame* coreFrame = core([self _frame]);
   2914     bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable();
   2915     bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
   2916         && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
   2917 
   2918     return selectionIsEditable && nextResponderIsInWebView;
   2919 }
   2920 
   2921 - (void)addMouseMovedObserver
   2922 {
   2923     if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications)
   2924         return;
   2925 
   2926     // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
   2927     if (!([[self window] isKeyWindow]
   2928 #if ENABLE(DASHBOARD_SUPPORT)
   2929             || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]
   2930 #endif
   2931         ))
   2932         return;
   2933 
   2934     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
   2935         name:WKMouseMovedNotification() object:nil];
   2936     [self _frameOrBoundsChanged];
   2937     _private->observingMouseMovedNotifications = true;
   2938 }
   2939 
   2940 - (void)removeMouseMovedObserver
   2941 {
   2942 #if ENABLE(DASHBOARD_SUPPORT)
   2943     // Don't remove the observer if we're running the Dashboard.
   2944     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
   2945         return;
   2946 #endif
   2947 
   2948     [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
   2949     [self _removeMouseMovedObserverUnconditionally];
   2950 }
   2951 
   2952 - (void)addSuperviewObservers
   2953 {
   2954     if (_private->observingSuperviewNotifications)
   2955         return;
   2956 
   2957     NSView *superview = [self superview];
   2958     if (!superview || ![self window])
   2959         return;
   2960 
   2961     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
   2962     [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview];
   2963     [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview];
   2964 
   2965     // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
   2966     // It will check the current scroll against the previous layout's scroll.  We need to
   2967     // do this here to catch the case where the WebView is laid out at one size, removed from its
   2968     // window, resized, and inserted into another window.  Our frame/bounds changed notifications
   2969     // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
   2970     [self _frameOrBoundsChanged];
   2971 
   2972     _private->observingSuperviewNotifications = true;
   2973 }
   2974 
   2975 - (void)addWindowObservers
   2976 {
   2977     if (_private->observingWindowNotifications)
   2978         return;
   2979 
   2980     NSWindow *window = [self window];
   2981     if (!window)
   2982         return;
   2983 
   2984     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
   2985     [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
   2986     [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
   2987     [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
   2988 
   2989     _private->observingWindowNotifications = true;
   2990 }
   2991 
   2992 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
   2993 {
   2994     [self _removeSuperviewObservers];
   2995 }
   2996 
   2997 - (void)viewDidMoveToSuperview
   2998 {
   2999     if ([self superview] != nil)
   3000         [self addSuperviewObservers];
   3001 
   3002 #if USE(ACCELERATED_COMPOSITING)
   3003     if ([self superview] && [self _isUsingAcceleratedCompositing]) {
   3004         WebView *webView = [self _webView];
   3005         if ([webView _postsAcceleratedCompositingNotifications])
   3006             [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:webView userInfo:nil];
   3007     }
   3008 #endif
   3009 }
   3010 
   3011 - (void)viewWillMoveToWindow:(NSWindow *)window
   3012 {
   3013     // Don't do anything if we aren't initialized.  This happens
   3014     // when decoding a WebView.  When WebViews are decoded their subviews
   3015     // are created by initWithCoder: and so won't be normally
   3016     // initialized.  The stub views are discarded by WebView.
   3017     if (!_private)
   3018         return;
   3019 
   3020     // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
   3021     [self _removeMouseMovedObserverUnconditionally];
   3022     [self _removeWindowObservers];
   3023     [self _removeSuperviewObservers];
   3024     [self _cancelUpdateMouseoverTimer];
   3025 
   3026     // FIXME: This accomplishes the same thing as the call to setCanStartMedia(false) in
   3027     // WebView. It would be nice to have a single mechanism instead of two.
   3028     [[self _pluginController] stopAllPlugins];
   3029 }
   3030 
   3031 - (void)viewDidMoveToWindow
   3032 {
   3033     // Don't do anything if we aren't initialized.  This happens
   3034     // when decoding a WebView.  When WebViews are decoded their subviews
   3035     // are created by initWithCoder: and so won't be normally
   3036     // initialized.  The stub views are discarded by WebView.
   3037     if (!_private || _private->closed)
   3038         return;
   3039 
   3040     [self _stopAutoscrollTimer];
   3041     if ([self window]) {
   3042         _private->lastScrollPosition = [[self superview] bounds].origin;
   3043         [self addWindowObservers];
   3044         [self addSuperviewObservers];
   3045         [self addMouseMovedObserver];
   3046 
   3047         // FIXME: This accomplishes the same thing as the call to setCanStartMedia(true) in
   3048         // WebView. It would be nice to have a single mechanism instead of two.
   3049         [[self _pluginController] startAllPlugins];
   3050 
   3051         _private->lastScrollPosition = NSZeroPoint;
   3052 
   3053 #if USE(ACCELERATED_COMPOSITING) && !defined(BUILDING_ON_LEOPARD)
   3054         // We may have created the layer hosting view while outside the window. Update the scale factor
   3055         // now that we have a window to get it from.
   3056         if (_private->layerHostingView) {
   3057             CGFloat scaleFactor;
   3058 #if !defined(BUILDING_ON_SNOW_LEOPARD)
   3059             scaleFactor = [[self window] backingScaleFactor];
   3060 #else
   3061             scaleFactor = [[self window] userSpaceScaleFactor];
   3062 #endif
   3063             [[_private->layerHostingView layer] setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)];
   3064         }
   3065 #endif
   3066     }
   3067 }
   3068 
   3069 - (void)_web_makePluginSubviewsPerformSelector:(SEL)selector withObject:(id)object
   3070 {
   3071 #if ENABLE(NETSCAPE_PLUGIN_API)
   3072     // Copy subviews because [self subviews] returns the view's mutable internal array,
   3073     // and we must avoid mutating the array while enumerating it.
   3074     NSArray *subviews = [[self subviews] copy];
   3075 
   3076     NSEnumerator *enumerator = [subviews objectEnumerator];
   3077     WebNetscapePluginView *view;
   3078     while ((view = [enumerator nextObject]) != nil)
   3079         if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
   3080             [view performSelector:selector withObject:object];
   3081 
   3082     [subviews release];
   3083 #endif
   3084 }
   3085 
   3086 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
   3087 {
   3088     [self _web_makePluginSubviewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
   3089 }
   3090 
   3091 - (void)viewDidMoveToHostWindow
   3092 {
   3093     [self _web_makePluginSubviewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
   3094 }
   3095 
   3096 
   3097 - (void)addSubview:(NSView *)view
   3098 {
   3099     [super addSubview:view];
   3100 
   3101     if ([WebPluginController isPlugInView:view])
   3102         [[self _pluginController] addPlugin:view];
   3103 }
   3104 
   3105 - (void)willRemoveSubview:(NSView *)subview
   3106 {
   3107 #ifndef NDEBUG
   3108     // Have to null-check _private, since this can be called via -dealloc when
   3109     // cleaning up the the layerHostingView.
   3110     if (_private && _private->enumeratingSubviews)
   3111         LOG(View, "A view of class %s was removed during subview enumeration for layout or printing mode change. We will still do layout or the printing mode change even though this view is no longer in the view hierarchy.", object_getClassName([subview class]));
   3112 #endif
   3113 
   3114     if ([WebPluginController isPlugInView:subview])
   3115         [[self _pluginController] destroyPlugin:subview];
   3116 
   3117     [super willRemoveSubview:subview];
   3118 }
   3119 
   3120 - (void)reapplyStyles
   3121 {
   3122 #ifdef LOG_TIMES
   3123     double start = CFAbsoluteTimeGetCurrent();
   3124 #endif
   3125 
   3126     if (Frame* coreFrame = core([self _frame]))
   3127         coreFrame->document()->styleSelectorChanged(RecalcStyleImmediately);
   3128 
   3129 #ifdef LOG_TIMES
   3130     double thisTime = CFAbsoluteTimeGetCurrent() - start;
   3131     LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
   3132 #endif
   3133 }
   3134 
   3135 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
   3136 // minPageWidth==0 implies a non-printing layout
   3137 - (void)layoutToMinimumPageWidth:(float)minPageLogicalWidth height:(float)minPageLogicalHeight maximumPageWidth:(float)maxPageLogicalWidth adjustingViewSize:(BOOL)adjustViewSize
   3138 {
   3139     if (![self _needsLayout])
   3140         return;
   3141 
   3142 #ifdef LOG_TIMES
   3143     double start = CFAbsoluteTimeGetCurrent();
   3144 #endif
   3145 
   3146     LOG(View, "%@ doing layout", self);
   3147 
   3148     Frame* coreFrame = core([self _frame]);
   3149     if (!coreFrame)
   3150         return;
   3151 
   3152     if (FrameView* coreView = coreFrame->view()) {
   3153         if (minPageLogicalWidth > 0.0) {
   3154             FloatSize pageSize(minPageLogicalWidth, minPageLogicalHeight);
   3155             if (coreFrame->document() && coreFrame->document()->renderView() && !coreFrame->document()->renderView()->style()->isHorizontalWritingMode())
   3156                 pageSize = FloatSize(minPageLogicalHeight, minPageLogicalWidth);
   3157             coreView->forceLayoutForPagination(pageSize, maxPageLogicalWidth / minPageLogicalWidth, adjustViewSize ? Frame::AdjustViewSize : Frame::DoNotAdjustViewSize);
   3158         } else {
   3159             coreView->forceLayout(!adjustViewSize);
   3160             if (adjustViewSize)
   3161                 coreView->adjustViewSize();
   3162         }
   3163     }
   3164 
   3165 #ifdef LOG_TIMES
   3166     double thisTime = CFAbsoluteTimeGetCurrent() - start;
   3167     LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
   3168 #endif
   3169 }
   3170 
   3171 - (void)layout
   3172 {
   3173     [self layoutToMinimumPageWidth:0 height:0 maximumPageWidth:0 adjustingViewSize:NO];
   3174 }
   3175 
   3176 // Deliver mouseup events to the DOM for button 2.
   3177 - (void)rightMouseUp:(NSEvent *)event
   3178 {
   3179     // There's a chance that if we run a nested event loop the event will be released.
   3180     // Retaining and then autoreleasing prevents that from causing a problem later here or
   3181     // inside AppKit code.
   3182     [[event retain] autorelease];
   3183 
   3184     [super rightMouseUp:event];
   3185 
   3186     if (Frame* coreframe = core([self _frame]))
   3187         coreframe->eventHandler()->mouseUp(event);
   3188 }
   3189 
   3190 static void setMenuItemTarget(NSMenuItem* menuItem)
   3191 {
   3192     // Don't set the menu item's action to the context menu action forwarder if we already
   3193     // have an action.
   3194     if ([menuItem action])
   3195         return;
   3196 
   3197     [menuItem setTarget:[WebMenuTarget sharedMenuTarget]];
   3198     [menuItem setAction:@selector(forwardContextMenuAction:)];
   3199 }
   3200 
   3201 static void setMenuTargets(NSMenu* menu)
   3202 {
   3203     NSInteger itemCount = [menu numberOfItems];
   3204     for (NSInteger i = 0; i < itemCount; ++i) {
   3205         NSMenuItem *item = [menu itemAtIndex:i];
   3206         setMenuItemTarget(item);
   3207         if ([item hasSubmenu])
   3208             setMenuTargets([item submenu]);
   3209     }
   3210 }
   3211 
   3212 - (NSMenu *)menuForEvent:(NSEvent *)event
   3213 {
   3214     // There's a chance that if we run a nested event loop the event will be released.
   3215     // Retaining and then autoreleasing prevents that from causing a problem later here or
   3216     // inside AppKit code.
   3217     [[event retain] autorelease];
   3218 
   3219     [_private->completionController endRevertingChange:NO moveLeft:NO];
   3220 
   3221     RefPtr<Frame> coreFrame = core([self _frame]);
   3222     if (!coreFrame)
   3223         return nil;
   3224 
   3225     Page* page = coreFrame->page();
   3226     if (!page)
   3227         return nil;
   3228 
   3229     // Match behavior of other browsers by sending a mousedown event for right clicks.
   3230     _private->handlingMouseDownEvent = YES;
   3231     page->contextMenuController()->clearContextMenu();
   3232     coreFrame->eventHandler()->mouseDown(event);
   3233     BOOL handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(event);
   3234     _private->handlingMouseDownEvent = NO;
   3235 
   3236     if (!handledEvent)
   3237         return nil;
   3238 
   3239     // Re-get page, since it might have gone away during event handling.
   3240     page = coreFrame->page();
   3241     if (!page)
   3242         return nil;
   3243 
   3244     ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
   3245     if (!coreMenu)
   3246         return nil;
   3247 
   3248     NSArray* menuItems = coreMenu->platformDescription();
   3249     if (!menuItems)
   3250         return nil;
   3251 
   3252     NSUInteger count = [menuItems count];
   3253     if (!count)
   3254         return nil;
   3255 
   3256     NSMenu* menu = [[[NSMenu alloc] init] autorelease];
   3257     for (NSUInteger i = 0; i < count; i++)
   3258         [menu addItem:[menuItems objectAtIndex:i]];
   3259     setMenuTargets(menu);
   3260 
   3261     [[WebMenuTarget sharedMenuTarget] setMenuController:page->contextMenuController()];
   3262 
   3263     return menu;
   3264 }
   3265 
   3266 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
   3267 {
   3268     return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
   3269 }
   3270 
   3271 - (void)clearFocus
   3272 {
   3273     Frame* coreFrame = core([self _frame]);
   3274     if (!coreFrame)
   3275         return;
   3276     Document* document = coreFrame->document();
   3277     if (!document)
   3278         return;
   3279 
   3280     document->setFocusedNode(0);
   3281 }
   3282 
   3283 - (BOOL)isOpaque
   3284 {
   3285     return [[self _webView] drawsBackground];
   3286 }
   3287 
   3288 #if !LOG_DISABLED
   3289 - (void)setNeedsDisplay:(BOOL)flag
   3290 {
   3291     LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO");
   3292     [super setNeedsDisplay:flag];
   3293 }
   3294 #endif
   3295 
   3296 #ifndef BUILDING_ON_TIGER
   3297 - (void)setNeedsDisplayInRect:(NSRect)invalidRect
   3298 {
   3299     if (_private->inScrollPositionChanged) {
   3300         // When scrolling, the dirty regions are adjusted for the scroll only
   3301         // after NSViewBoundsDidChangeNotification is sent. Translate the invalid
   3302         // rect to pre-scrolled coordinates in order to get the right dirty region
   3303         // after adjustment. See <rdar://problem/7678927>.
   3304         NSPoint origin = [[self superview] bounds].origin;
   3305         invalidRect.origin.x -= _private->lastScrollPosition.x - origin.x;
   3306         invalidRect.origin.y -= _private->lastScrollPosition.y - origin.y;
   3307     }
   3308     [super setNeedsDisplayInRect:invalidRect];
   3309 }
   3310 #endif
   3311 
   3312 - (void)setNeedsLayout: (BOOL)flag
   3313 {
   3314     LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO");
   3315     if (!flag)
   3316         return; // There's no way to say you don't need a layout.
   3317     if (Frame* frame = core([self _frame])) {
   3318         if (frame->document() && frame->document()->inPageCache())
   3319             return;
   3320         if (FrameView* view = frame->view())
   3321             view->setNeedsLayout();
   3322     }
   3323 }
   3324 
   3325 - (void)setNeedsToApplyStyles: (BOOL)flag
   3326 {
   3327     LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO");
   3328     if (!flag)
   3329         return; // There's no way to say you don't need a style recalc.
   3330     if (Frame* frame = core([self _frame])) {
   3331         if (frame->document() && frame->document()->inPageCache())
   3332             return;
   3333         frame->document()->scheduleForcedStyleRecalc();
   3334     }
   3335 }
   3336 
   3337 - (void)drawSingleRect:(NSRect)rect
   3338 {
   3339     [NSGraphicsContext saveGraphicsState];
   3340     NSRectClip(rect);
   3341 
   3342     ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
   3343 
   3344     [(WebClipView *)[self superview] setAdditionalClip:rect];
   3345 
   3346     @try {
   3347         if ([self _transparentBackground]) {
   3348             [[NSColor clearColor] set];
   3349             NSRectFill (rect);
   3350         }
   3351 
   3352         [[self _frame] _drawRect:rect contentsOnly:YES];
   3353 
   3354         WebView *webView = [self _webView];
   3355 
   3356         // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
   3357         // called after the WebView has closed. If the client did not properly close the WebView and set the
   3358         // UIDelegate to nil, then the UIDelegate will be stale and this code will crash.
   3359         static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
   3360         if (version3OrLaterClient)
   3361             [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
   3362 
   3363         if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight])
   3364             [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]];
   3365 
   3366         [(WebClipView *)[self superview] resetAdditionalClip];
   3367 
   3368         [NSGraphicsContext restoreGraphicsState];
   3369     } @catch (NSException *localException) {
   3370         [(WebClipView *)[self superview] resetAdditionalClip];
   3371         [NSGraphicsContext restoreGraphicsState];
   3372         LOG_ERROR("Exception caught while drawing: %@", localException);
   3373         [localException raise];
   3374     }
   3375 }
   3376 
   3377 - (void)drawRect:(NSRect)rect
   3378 {
   3379     ASSERT_MAIN_THREAD();
   3380     LOG(View, "%@ drawing", self);
   3381 
   3382     const NSRect *rects;
   3383     NSInteger count;
   3384     [self getRectsBeingDrawn:&rects count:&count];
   3385 
   3386     BOOL subviewsWereSetAside = _private->subviewsSetAside;
   3387     if (subviewsWereSetAside)
   3388         [self _restoreSubviews];
   3389 
   3390 #ifdef LOG_TIMES
   3391     double start = CFAbsoluteTimeGetCurrent();
   3392 #endif
   3393 
   3394     WebView *webView = [self _webView];
   3395     if ([webView _mustDrawUnionedRect:rect singleRects:rects count:count])
   3396         [self drawSingleRect:rect];
   3397     else
   3398         for (int i = 0; i < count; ++i)
   3399             [self drawSingleRect:rects[i]];
   3400 
   3401 #ifdef LOG_TIMES
   3402     double thisTime = CFAbsoluteTimeGetCurrent() - start;
   3403     LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
   3404 #endif
   3405 
   3406     if (subviewsWereSetAside)
   3407         [self _setAsideSubviews];
   3408 
   3409 #if USE(ACCELERATED_COMPOSITING)
   3410     // Only do the synchronization dance if we're drawing into the window, otherwise
   3411     // we risk disabling screen updates when no flush is pending.
   3412     if ([NSGraphicsContext currentContext] == [[self window] graphicsContext] && [webView _needsOneShotDrawingSynchronization]) {
   3413         // Disable screen updates to minimize the chances of the race between the CA
   3414         // display link and AppKit drawing causing flashes.
   3415         [[self window] disableScreenUpdatesUntilFlush];
   3416 
   3417         // Make sure any layer changes that happened as a result of layout
   3418         // via -viewWillDraw are committed.
   3419         [CATransaction flush];
   3420         [webView _setNeedsOneShotDrawingSynchronization:NO];
   3421     }
   3422 #endif
   3423 
   3424     if (webView)
   3425         CallUIDelegate(webView, @selector(webView:didDrawFrame:), [self _frame]);
   3426 }
   3427 
   3428 // Turn off the additional clip while computing our visibleRect.
   3429 - (NSRect)visibleRect
   3430 {
   3431     if (!([[self superview] isKindOfClass:[WebClipView class]]))
   3432         return [super visibleRect];
   3433 
   3434     WebClipView *clipView = (WebClipView *)[self superview];
   3435 
   3436     BOOL hasAdditionalClip = [clipView hasAdditionalClip];
   3437     if (!hasAdditionalClip) {
   3438         return [super visibleRect];
   3439     }
   3440 
   3441     NSRect additionalClip = [clipView additionalClip];
   3442     [clipView resetAdditionalClip];
   3443     NSRect visibleRect = [super visibleRect];
   3444     [clipView setAdditionalClip:additionalClip];
   3445     return visibleRect;
   3446 }
   3447 
   3448 - (void)_invalidateGStatesForTree
   3449 {
   3450     // AppKit is in the process of traversing the NSView tree, and is going to send -renewGState to
   3451     // descendants, including plug-in views. This can result in calls out to plug-in code and back into
   3452     // WebCore via JavaScript, which could normally mutate the NSView tree while it is being traversed.
   3453     // Defer those mutations while descendants are being traveresed.
   3454     RenderWidget::suspendWidgetHierarchyUpdates();
   3455     [super _invalidateGStatesForTree];
   3456     RenderWidget::resumeWidgetHierarchyUpdates();
   3457 }
   3458 
   3459 - (BOOL)isFlipped
   3460 {
   3461     return YES;
   3462 }
   3463 
   3464 - (void)windowDidBecomeKey:(NSNotification *)notification
   3465 {
   3466     if (!pthread_main_np()) {
   3467         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
   3468         return;
   3469     }
   3470 
   3471     NSWindow *keyWindow = [notification object];
   3472 
   3473     if (keyWindow == [self window]) {
   3474         [self addMouseMovedObserver];
   3475         [self _updateSecureInputState];
   3476     }
   3477 }
   3478 
   3479 - (void)windowDidResignKey:(NSNotification *)notification
   3480 {
   3481     if (!pthread_main_np()) {
   3482         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
   3483         return;
   3484     }
   3485 
   3486     NSWindow *formerKeyWindow = [notification object];
   3487 
   3488     if (formerKeyWindow == [self window])
   3489         [self removeMouseMovedObserver];
   3490 
   3491     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
   3492         [self _updateSecureInputState];
   3493         [_private->completionController endRevertingChange:NO moveLeft:NO];
   3494     }
   3495 }
   3496 
   3497 - (void)windowWillClose:(NSNotification *)notification
   3498 {
   3499     if (!pthread_main_np()) {
   3500         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
   3501         return;
   3502     }
   3503 
   3504     [_private->completionController endRevertingChange:NO moveLeft:NO];
   3505     [[self _pluginController] destroyAllPlugins];
   3506 }
   3507 
   3508 - (void)scrollWheel:(NSEvent *)event
   3509 {
   3510     // There's a chance that responding to this event will run a nested event loop, and
   3511     // fetching a new event might release the old one. Retaining and then autoreleasing
   3512     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   3513     [[event retain] autorelease];
   3514 
   3515     Frame* frame = core([self _frame]);
   3516     if (!frame || !frame->eventHandler()->wheelEvent(event))
   3517         [super scrollWheel:event];
   3518 }
   3519 
   3520 - (BOOL)_isSelectionEvent:(NSEvent *)event
   3521 {
   3522     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
   3523     return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
   3524 }
   3525 
   3526 - (BOOL)_isScrollBarEvent:(NSEvent *)event
   3527 {
   3528     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
   3529     return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsInScrollBarKey] boolValue];
   3530 }
   3531 
   3532 - (BOOL)acceptsFirstMouse:(NSEvent *)event
   3533 {
   3534     // There's a chance that responding to this event will run a nested event loop, and
   3535     // fetching a new event might release the old one. Retaining and then autoreleasing
   3536     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   3537     [[event retain] autorelease];
   3538 
   3539     NSView *hitView = [self _hitViewForEvent:event];
   3540     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
   3541 
   3542 #if ENABLE(DASHBOARD_SUPPORT)
   3543     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
   3544         return YES;
   3545 #endif
   3546 
   3547     if (hitHTMLView) {
   3548         bool result = false;
   3549         if (Frame* coreFrame = core([hitHTMLView _frame])) {
   3550             coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
   3551             [hitHTMLView _setMouseDownEvent:event];
   3552             if ([hitHTMLView _isSelectionEvent:event])
   3553                 result = coreFrame->eventHandler()->eventMayStartDrag(event);
   3554             else if ([hitHTMLView _isScrollBarEvent:event])
   3555                 result = true;
   3556             [hitHTMLView _setMouseDownEvent:nil];
   3557         }
   3558         return result;
   3559     }
   3560     return [hitView acceptsFirstMouse:event];
   3561 }
   3562 
   3563 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
   3564 {
   3565     // There's a chance that responding to this event will run a nested event loop, and
   3566     // fetching a new event might release the old one. Retaining and then autoreleasing
   3567     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   3568     [[event retain] autorelease];
   3569 
   3570     NSView *hitView = [self _hitViewForEvent:event];
   3571     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
   3572     if (hitHTMLView) {
   3573         bool result = false;
   3574         if ([hitHTMLView _isSelectionEvent:event]) {
   3575             if (Frame* coreFrame = core([hitHTMLView _frame])) {
   3576                 [hitHTMLView _setMouseDownEvent:event];
   3577                 result = coreFrame->eventHandler()->eventMayStartDrag(event);
   3578                 [hitHTMLView _setMouseDownEvent:nil];
   3579             }
   3580         }
   3581         return result;
   3582     }
   3583     return [hitView shouldDelayWindowOrderingForEvent:event];
   3584 }
   3585 
   3586 - (void)mouseDown:(NSEvent *)event
   3587 {
   3588     // There's a chance that responding to this event will run a nested event loop, and
   3589     // fetching a new event might release the old one. Retaining and then autoreleasing
   3590     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   3591     [[event retain] autorelease];
   3592 
   3593     RetainPtr<WebHTMLView> protector = self;
   3594     if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
   3595         return;
   3596 
   3597     _private->handlingMouseDownEvent = YES;
   3598 
   3599     // Record the mouse down position so we can determine drag hysteresis.
   3600     [self _setMouseDownEvent:event];
   3601 
   3602     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
   3603     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
   3604         goto done;
   3605 
   3606     [_private->completionController endRevertingChange:NO moveLeft:NO];
   3607 
   3608     // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
   3609     // We don't want to pass them along to KHTML a second time.
   3610     if (!([event modifierFlags] & NSControlKeyMask)) {
   3611         _private->ignoringMouseDraggedEvents = NO;
   3612 
   3613         // Don't do any mouseover while the mouse is down.
   3614         [self _cancelUpdateMouseoverTimer];
   3615 
   3616         // Let WebCore get a chance to deal with the event. This will call back to us
   3617         // to start the autoscroll timer if appropriate.
   3618         if (Frame* coreframe = core([self _frame]))
   3619             coreframe->eventHandler()->mouseDown(event);
   3620     }
   3621 
   3622 done:
   3623     _private->handlingMouseDownEvent = NO;
   3624 }
   3625 
   3626 - (void)dragImage:(NSImage *)dragImage
   3627                at:(NSPoint)at
   3628            offset:(NSSize)offset
   3629             event:(NSEvent *)event
   3630        pasteboard:(NSPasteboard *)pasteboard
   3631            source:(id)source
   3632         slideBack:(BOOL)slideBack
   3633 {
   3634     ASSERT(self == [self _topHTMLView]);
   3635     [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
   3636 }
   3637 
   3638 - (void)mouseDragged:(NSEvent *)event
   3639 {
   3640     // There's a chance that responding to this event will run a nested event loop, and
   3641     // fetching a new event might release the old one. Retaining and then autoreleasing
   3642     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   3643     [[event retain] autorelease];
   3644 
   3645     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
   3646     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
   3647         return;
   3648 
   3649     [self retain];
   3650 
   3651     if (!_private->ignoringMouseDraggedEvents) {
   3652         if (Frame* frame = core([self _frame])) {
   3653             if (Page* page = frame->page())
   3654                 page->mainFrame()->eventHandler()->mouseDragged(event);
   3655         }
   3656     }
   3657 
   3658     [self release];
   3659 }
   3660 
   3661 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
   3662 {
   3663     ASSERT(![self _webView] || [self _isTopHTMLView]);
   3664 
   3665     Page* page = core([self _webView]);
   3666     if (!page)
   3667         return NSDragOperationNone;
   3668 
   3669     return (NSDragOperation)page->dragController()->sourceDragOperation();
   3670 }
   3671 
   3672 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
   3673 {
   3674     ASSERT(![self _webView] || [self _isTopHTMLView]);
   3675 
   3676     NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
   3677     NSPoint windowMouseLoc = windowImageLoc;
   3678 
   3679     if (Page* page = core([self _webView])) {
   3680         DragController* dragController = page->dragController();
   3681         windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
   3682         dragController->dragEnded();
   3683     }
   3684 
   3685     [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation];
   3686 
   3687     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
   3688     _private->ignoringMouseDraggedEvents = YES;
   3689 
   3690     // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
   3691     // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
   3692     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
   3693                                             location:windowMouseLoc
   3694                                        modifierFlags:[[NSApp currentEvent] modifierFlags]
   3695                                            timestamp:[NSDate timeIntervalSinceReferenceDate]
   3696                                         windowNumber:[[self window] windowNumber]
   3697                                              context:[[NSApp currentEvent] context]
   3698                                          eventNumber:0 clickCount:0 pressure:0];
   3699     [self mouseUp:fakeEvent]; // This will also update the mouseover state.
   3700 }
   3701 
   3702 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
   3703 {
   3704     NSFileWrapper *wrapper = nil;
   3705     NSURL *draggingImageURL = nil;
   3706 
   3707     if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) {
   3708 
   3709         SharedBuffer *buffer = static_cast<CachedResource*>(tiffResource)->data();
   3710         if (!buffer)
   3711             goto noPromisedData;
   3712 
   3713         NSData *data = buffer->createNSData();
   3714         NSURLResponse *response = tiffResource->response().nsURLResponse();
   3715         draggingImageURL = [response URL];
   3716         wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
   3717         NSString* filename = [response suggestedFilename];
   3718         NSString* trueExtension(tiffResource->image()->filenameExtension());
   3719         if (!matchesExtensionOrEquivalent(filename, trueExtension))
   3720             filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension];
   3721         [wrapper setPreferredFilename:filename];
   3722     }
   3723 
   3724 noPromisedData:
   3725 
   3726     if (!wrapper) {
   3727         ASSERT(![self _webView] || [self _isTopHTMLView]);
   3728         Page* page = core([self _webView]);
   3729 
   3730         //If a load occurs midway through a drag, the view may be detached, which gives
   3731         //us no ability to get to the original Page, so we cannot access any drag state
   3732         //FIXME: is there a way to recover?
   3733         if (!page)
   3734             return nil;
   3735 
   3736         const KURL& imageURL = page->dragController()->draggingImageURL();
   3737         ASSERT(!imageURL.isEmpty());
   3738         draggingImageURL = imageURL;
   3739 
   3740         wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL];
   3741     }
   3742 
   3743     if (wrapper == nil) {
   3744         LOG_ERROR("Failed to create image file.");
   3745         return nil;
   3746     }
   3747 
   3748     // FIXME: Report an error if we fail to create a file.
   3749     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
   3750     path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
   3751     if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
   3752         LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
   3753 
   3754     if (draggingImageURL)
   3755         [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path];
   3756 
   3757     return [NSArray arrayWithObject:[path lastPathComponent]];
   3758 }
   3759 
   3760 - (void)mouseUp:(NSEvent *)event
   3761 {
   3762     // There's a chance that responding to this event will run a nested event loop, and
   3763     // fetching a new event might release the old one. Retaining and then autoreleasing
   3764     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   3765     [[event retain] autorelease];
   3766 
   3767     [self _setMouseDownEvent:nil];
   3768 
   3769     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
   3770     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
   3771         return;
   3772 
   3773     [self retain];
   3774 
   3775     [self _stopAutoscrollTimer];
   3776     if (Frame* frame = core([self _frame])) {
   3777         if (Page* page = frame->page())
   3778             page->mainFrame()->eventHandler()->mouseUp(event);
   3779     }
   3780     [self _updateMouseoverWithFakeEvent];
   3781 
   3782     [self release];
   3783 }
   3784 
   3785 - (void)mouseMovedNotification:(NSNotification *)notification
   3786 {
   3787     [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
   3788 }
   3789 
   3790 // returning YES from this method is the way we tell AppKit that it is ok for this view
   3791 // to be in the key loop even when "tab to all controls" is not on.
   3792 - (BOOL)needsPanelToBecomeKey
   3793 {
   3794     return YES;
   3795 }
   3796 
   3797 // Utility function to make sure we don't return anything through the NSTextInput
   3798 // API when an editable region is not currently focused.
   3799 static BOOL isTextInput(Frame* coreFrame)
   3800 {
   3801     return coreFrame && !coreFrame->selection()->isNone() && coreFrame->selection()->isContentEditable();
   3802 }
   3803 
   3804 static BOOL isInPasswordField(Frame* coreFrame)
   3805 {
   3806     return coreFrame && coreFrame->selection()->isInPasswordField();
   3807 }
   3808 
   3809 - (BOOL)becomeFirstResponder
   3810 {
   3811     NSSelectionDirection direction = NSDirectSelection;
   3812     if (![[self _webView] _isPerformingProgrammaticFocus])
   3813         direction = [[self window] keyViewSelectionDirection];
   3814 
   3815     [self _updateFontPanel];
   3816 
   3817     Frame* frame = core([self _frame]);
   3818     if (!frame)
   3819         return YES;
   3820 
   3821     BOOL exposeInputContext = isTextInput(frame) && !isInPasswordField(frame);
   3822     if (exposeInputContext != _private->exposeInputContext) {
   3823         _private->exposeInputContext = exposeInputContext;
   3824         [NSApp updateWindows];
   3825     }
   3826 
   3827     _private->_forceUpdateSecureInputState = YES;
   3828     [self _updateSecureInputState];
   3829     _private->_forceUpdateSecureInputState = NO;
   3830 
   3831     frame->editor()->setStartNewKillRingSequence(true);
   3832 
   3833     Page* page = frame->page();
   3834     if (!page)
   3835         return YES;
   3836 
   3837     if (![[self _webView] _isPerformingProgrammaticFocus])
   3838         page->focusController()->setFocusedFrame(frame);
   3839 
   3840     page->focusController()->setFocused(true);
   3841 
   3842     if (direction == NSDirectSelection)
   3843         return YES;
   3844 
   3845     if (Document* document = frame->document())
   3846         document->setFocusedNode(0);
   3847     page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward,
   3848                                              frame->eventHandler()->currentKeyboardEvent().get());
   3849     return YES;
   3850 }
   3851 
   3852 - (BOOL)resignFirstResponder
   3853 {
   3854     BOOL resign = [super resignFirstResponder];
   3855     if (resign) {
   3856         if (_private->isInSecureInputState) {
   3857             DisableSecureEventInput();
   3858             _private->isInSecureInputState = NO;
   3859         }
   3860         [_private->completionController endRevertingChange:NO moveLeft:NO];
   3861         Frame* coreFrame = core([self _frame]);
   3862         if (!coreFrame)
   3863             return resign;
   3864         Page* page = coreFrame->page();
   3865         if (!page)
   3866             return resign;
   3867         if (![self maintainsInactiveSelection]) {
   3868             [self deselectAll];
   3869             if (![[self _webView] _isPerformingProgrammaticFocus])
   3870                 [self clearFocus];
   3871         }
   3872 
   3873         id nextResponder = [[self window] _newFirstResponderAfterResigning];
   3874         bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
   3875             && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
   3876         if (!nextResponderIsInWebView)
   3877             page->focusController()->setFocused(false);
   3878     }
   3879     return resign;
   3880 }
   3881 
   3882 - (void)setDataSource:(WebDataSource *)dataSource
   3883 {
   3884     ASSERT(dataSource);
   3885     if (_private->dataSource != dataSource) {
   3886         ASSERT(!_private->closed);
   3887         BOOL hadDataSource = _private->dataSource != nil;
   3888 
   3889         [dataSource retain];
   3890         [_private->dataSource release];
   3891         _private->dataSource = dataSource;
   3892         [_private->pluginController setDataSource:dataSource];
   3893 
   3894         if (!hadDataSource)
   3895             [self addMouseMovedObserver];
   3896     }
   3897 }
   3898 
   3899 - (void)dataSourceUpdated:(WebDataSource *)dataSource
   3900 {
   3901 }
   3902 
   3903 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
   3904 // key.  WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
   3905 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
   3906 - (void)updateCell:(NSCell*)cell
   3907 {
   3908 }
   3909 
   3910 // Does setNeedsDisplay:NO as a side effect when printing is ending.
   3911 // pageWidth != 0 implies we will relayout to a new width
   3912 - (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageLogicalWidth logicalHeight:(float)minPageLogicalHeight maximumPageLogicalWidth:(float)maxPageLogicalWidth adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent
   3913 {
   3914     if (printing == _private->printing && paginateScreenContent == _private->paginateScreenContent)
   3915         return;
   3916 
   3917     WebFrame *frame = [self _frame];
   3918     NSArray *subframes = [frame childFrames];
   3919     unsigned n = [subframes count];
   3920     unsigned i;
   3921     for (i = 0; i != n; ++i) {
   3922         WebFrame *subframe = [subframes objectAtIndex:i];
   3923         WebFrameView *frameView = [subframe frameView];
   3924         if ([[subframe _dataSource] _isDocumentHTML]) {
   3925             [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:adjustViewSize paginateScreenContent:paginateScreenContent];
   3926         }
   3927     }
   3928 
   3929     [_private->pageRects release];
   3930     _private->pageRects = nil;
   3931     _private->printing = printing;
   3932     _private->paginateScreenContent = paginateScreenContent;
   3933 
   3934     Frame* coreFrame = core([self _frame]);
   3935     if (coreFrame) {
   3936         if (FrameView* coreView = coreFrame->view())
   3937             coreView->setMediaType(_private->printing ? "print" : "screen");
   3938         if (Document* document = coreFrame->document()) {
   3939             document->setPaginatedForScreen(_private->paginateScreenContent);
   3940             document->setPrinting(_private->printing);
   3941             document->styleSelectorChanged(RecalcStyleImmediately);
   3942         }
   3943     }
   3944 
   3945     [self setNeedsLayout:YES];
   3946     [self layoutToMinimumPageWidth:minPageLogicalWidth height:minPageLogicalHeight maximumPageWidth:maxPageLogicalWidth adjustingViewSize:adjustViewSize];
   3947     if (!printing) {
   3948         // Can't do this when starting printing or nested printing won't work, see 3491427.
   3949         [self setNeedsDisplay:NO];
   3950     }
   3951 }
   3952 
   3953 - (BOOL)canPrintHeadersAndFooters
   3954 {
   3955     return YES;
   3956 }
   3957 
   3958 // This is needed for the case where the webview is embedded in the view that's being printed.
   3959 // It shouldn't be called when the webview is being printed directly.
   3960 - (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit
   3961 {
   3962     // This helps when we print as part of a larger print process.
   3963     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
   3964     BOOL wasInPrintingMode = _private->printing;
   3965     if (!wasInPrintingMode)
   3966         [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
   3967 
   3968     *newBottom = [self _adjustedBottomOfPageWithTop:oldTop bottom:oldBottom limit:bottomLimit];
   3969 
   3970     if (!wasInPrintingMode) {
   3971         NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation];
   3972         if (currenPrintOperation)
   3973             // delay _setPrinting:NO until back to main loop as this method may get called repeatedly
   3974             [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0];
   3975         else
   3976             // not sure if this is actually ever invoked, it probably shouldn't be
   3977             [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
   3978     }
   3979 }
   3980 
   3981 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
   3982 {
   3983     bool useViewWidth = true;
   3984     Frame* coreFrame = core([self _frame]);
   3985     if (coreFrame) {
   3986         Document* document = coreFrame->document();
   3987         if (document && document->renderView())
   3988             useViewWidth = document->renderView()->style()->isHorizontalWritingMode();
   3989     }
   3990 
   3991     float viewLogicalWidth = useViewWidth ? NSWidth([self bounds]) : NSHeight([self bounds]);
   3992     if (viewLogicalWidth < 1) {
   3993         LOG_ERROR("%@ has no logical width when printing", self);
   3994         return 1.0f;
   3995     }
   3996 
   3997     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
   3998     float maxShrinkToFitScaleFactor = 1.0f / _WebHTMLViewPrintingMaximumShrinkFactor;
   3999     float shrinkToFitScaleFactor = (useViewWidth ? [printOperation _web_availablePaperWidth] :  [printOperation _web_availablePaperHeight]) / viewLogicalWidth;
   4000     return userScaleFactor * max(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor);
   4001 }
   4002 
   4003 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
   4004 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
   4005 // if AppKit makes it SPI/API.
   4006 - (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation
   4007 {
   4008     return [self _scaleFactorForPrintOperation:printOperation];
   4009 }
   4010 
   4011 // This is used for Carbon printing. At some point we might want to make this public API.
   4012 - (void)setPageWidthForPrinting:(float)pageWidth
   4013 {
   4014     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
   4015     [self _setPrinting:YES minimumPageLogicalWidth:pageWidth logicalHeight:0 maximumPageLogicalWidth:pageWidth adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
   4016 }
   4017 
   4018 - (void)_endPrintModeAndRestoreWindowAutodisplay
   4019 {
   4020     [self _endPrintMode];
   4021     [[self window] setAutodisplay:YES];
   4022 }
   4023 
   4024 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
   4025 {
   4026     ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
   4027     NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
   4028     if (initiatingOperation == currentOperation) {
   4029         // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
   4030         // being extra paranoid here since the printing code is so fragile. Delay the cleanup
   4031         // further.
   4032         ASSERT_NOT_REACHED();
   4033         [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
   4034     } else if ([currentOperation view] == self) {
   4035         // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
   4036         // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
   4037         // fragile. Do nothing, because we don't want to break the print job currently in progress, and
   4038         // the print job currently in progress is responsible for its own cleanup.
   4039         ASSERT_NOT_REACHED();
   4040     } else {
   4041         // The print job that kicked off this delayed call has finished, and this view is not being
   4042         // printed again. We expect that no other print job has started. Since this delayed call wasn't
   4043         // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
   4044         // the print mode here.
   4045         ASSERT(currentOperation == nil);
   4046         [self _endPrintModeAndRestoreWindowAutodisplay];
   4047     }
   4048 }
   4049 
   4050 // Return the number of pages available for printing
   4051 - (BOOL)knowsPageRange:(NSRangePointer)range
   4052 {
   4053     // Must do this explicit display here, because otherwise the view might redisplay while the print
   4054     // sheet was up, using printer fonts (and looking different).
   4055     [self displayIfNeeded];
   4056     [[self window] setAutodisplay:NO];
   4057 
   4058     [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
   4059     NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
   4060     if (![self _beginPrintModeWithPageWidth:[printOperation _web_availablePaperWidth] height:[printOperation _web_availablePaperHeight] shrinkToFit:YES])
   4061         return NO;
   4062 
   4063     // Certain types of errors, including invalid page ranges, can cause beginDocument and
   4064     // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
   4065     // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
   4066     // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
   4067     // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
   4068     // which is after beginDocument and endDocument would be called.
   4069     [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
   4070 
   4071     // There is a theoretical chance that someone could do some drawing between here and endDocument,
   4072     // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
   4073     // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
   4074 
   4075     range->location = 1;
   4076     float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
   4077     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
   4078     [_private->pageRects release];
   4079     float fullPageWidth = floorf([printOperation _web_availablePaperWidth] / totalScaleFactor);
   4080     float fullPageHeight = floorf([printOperation _web_availablePaperHeight] / totalScaleFactor);
   4081     WebFrame *frame = [self _frame];
   4082     NSArray *newPageRects = [frame _computePageRectsWithPrintScaleFactor:userScaleFactor pageSize:NSMakeSize(fullPageWidth, fullPageHeight)];
   4083 
   4084     // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
   4085     // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
   4086     // a blank page (with correct-looking header and footer if that option is on), which matches
   4087     // the behavior of IE and Camino at least.
   4088     if ([newPageRects count] == 0)
   4089         newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
   4090 
   4091     _private->pageRects = [newPageRects retain];
   4092 
   4093     range->length = [_private->pageRects count];
   4094 
   4095     return YES;
   4096 }
   4097 
   4098 // Return the drawing rectangle for a particular page number
   4099 - (NSRect)rectForPage:(NSInteger)page
   4100 {
   4101     return [[_private->pageRects objectAtIndex:page - 1] rectValue];
   4102 }
   4103 
   4104 - (void)drawPageBorderWithSize:(NSSize)borderSize
   4105 {
   4106     ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));
   4107     [[self _webView] _drawHeaderAndFooter];
   4108 }
   4109 
   4110 - (void)beginDocument
   4111 {
   4112     @try {
   4113         // From now on we'll get a chance to call _endPrintMode in either beginDocument or
   4114         // endDocument, so we can cancel the "just in case" pending call.
   4115         [NSObject cancelPreviousPerformRequestsWithTarget:self
   4116                                                  selector:@selector(_delayedEndPrintMode:)
   4117                                                    object:[NSPrintOperation currentOperation]];
   4118         [super beginDocument];
   4119     } @catch (NSException *localException) {
   4120         // Exception during [super beginDocument] means that endDocument will not get called,
   4121         // so we need to clean up our "print mode" here.
   4122         [self _endPrintModeAndRestoreWindowAutodisplay];
   4123     }
   4124 }
   4125 
   4126 - (void)endDocument
   4127 {
   4128     [super endDocument];
   4129     // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO
   4130     [self _endPrintModeAndRestoreWindowAutodisplay];
   4131 }
   4132 
   4133 - (void)keyDown:(NSEvent *)event
   4134 {
   4135     // There's a chance that responding to this event will run a nested event loop, and
   4136     // fetching a new event might release the old one. Retaining and then autoreleasing
   4137     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   4138     [[event retain] autorelease];
   4139 
   4140     RetainPtr<WebHTMLView> selfProtector = self;
   4141     BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
   4142 
   4143     BOOL callSuper = NO;
   4144 
   4145     [_private->keyDownEvent release];
   4146     _private->keyDownEvent = [event retain];
   4147 
   4148     BOOL completionPopupWasOpen = _private->completionController && [_private->completionController popupWindowIsOpen];
   4149     Frame* coreFrame = core([self _frame]);
   4150     if (!eventWasSentToWebCore && coreFrame && coreFrame->eventHandler()->keyEvent(event)) {
   4151         // WebCore processed a key event, bail on any preexisting complete: UI
   4152         if (completionPopupWasOpen)
   4153             [_private->completionController endRevertingChange:YES moveLeft:NO];
   4154     } else if (!_private->completionController || ![_private->completionController filterKeyDown:event]) {
   4155         // Not consumed by complete: popup window
   4156         [_private->completionController endRevertingChange:YES moveLeft:NO];
   4157         callSuper = YES;
   4158     }
   4159     if (callSuper)
   4160         [super keyDown:event];
   4161     else
   4162         [NSCursor setHiddenUntilMouseMoves:YES];
   4163 }
   4164 
   4165 - (void)keyUp:(NSEvent *)event
   4166 {
   4167     // There's a chance that responding to this event will run a nested event loop, and
   4168     // fetching a new event might release the old one. Retaining and then autoreleasing
   4169     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   4170     [[event retain] autorelease];
   4171 
   4172     BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
   4173 
   4174     RetainPtr<WebHTMLView> selfProtector = self;
   4175     Frame* coreFrame = core([self _frame]);
   4176     if (coreFrame && !eventWasSentToWebCore)
   4177         coreFrame->eventHandler()->keyEvent(event);
   4178     else
   4179         [super keyUp:event];
   4180 }
   4181 
   4182 - (void)flagsChanged:(NSEvent *)event
   4183 {
   4184     // There's a chance that responding to this event will run a nested event loop, and
   4185     // fetching a new event might release the old one. Retaining and then autoreleasing
   4186     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   4187     [[event retain] autorelease];
   4188 
   4189     RetainPtr<WebHTMLView> selfProtector = self;
   4190 
   4191     Frame* coreFrame = core([self _frame]);
   4192     unsigned short keyCode = [event keyCode];
   4193 
   4194     // Don't make an event from the num lock and function keys.
   4195     if (coreFrame && keyCode != 0 && keyCode != 10 && keyCode != 63) {
   4196         coreFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(event));
   4197         return;
   4198     }
   4199 
   4200     [super flagsChanged:event];
   4201 }
   4202 
   4203 - (id)accessibilityAttributeValue:(NSString*)attributeName
   4204 {
   4205     if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
   4206         id accTree = [[self _frame] accessibilityRoot];
   4207         if (accTree)
   4208             return [NSArray arrayWithObject:accTree];
   4209         return nil;
   4210     }
   4211     return [super accessibilityAttributeValue:attributeName];
   4212 }
   4213 
   4214 - (id)accessibilityFocusedUIElement
   4215 {
   4216     id accTree = [[self _frame] accessibilityRoot];
   4217     if (accTree)
   4218         return [accTree accessibilityFocusedUIElement];
   4219     return self;
   4220 }
   4221 
   4222 - (id)accessibilityHitTest:(NSPoint)point
   4223 {
   4224     id accTree = [[self _frame] accessibilityRoot];
   4225     if (accTree) {
   4226         NSPoint windowCoord = [[self window] convertScreenToBase:point];
   4227         return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
   4228     }
   4229     return self;
   4230 }
   4231 
   4232 - (id)_accessibilityParentForSubview:(NSView *)subview
   4233 {
   4234     id accTree = [[self _frame] accessibilityRoot];
   4235     if (!accTree)
   4236         return self;
   4237     id parent = [accTree _accessibilityParentForSubview:subview];
   4238     if (!parent)
   4239         return self;
   4240     return parent;
   4241 }
   4242 
   4243 - (void)centerSelectionInVisibleArea:(id)sender
   4244 {
   4245     COMMAND_PROLOGUE
   4246 
   4247     if (Frame* coreFrame = core([self _frame]))
   4248         coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways);
   4249 }
   4250 
   4251 - (NSData *)_selectionStartFontAttributesAsRTF
   4252 {
   4253     Frame* coreFrame = core([self _frame]);
   4254     NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x"
   4255         attributes:coreFrame ? coreFrame->editor()->fontAttributesForSelectionStart() : nil];
   4256     NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
   4257     [string release];
   4258     return data;
   4259 }
   4260 
   4261 - (NSDictionary *)_fontAttributesFromFontPasteboard
   4262 {
   4263     NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
   4264     if (fontPasteboard == nil)
   4265         return nil;
   4266     NSData *data = [fontPasteboard dataForType:NSFontPboardType];
   4267     if (data == nil || [data length] == 0)
   4268         return nil;
   4269     // NSTextView does something more efficient by parsing the attributes only, but that's not available in API.
   4270     NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease];
   4271     if (string == nil || [string length] == 0)
   4272         return nil;
   4273     return [string fontAttributesInRange:NSMakeRange(0, 1)];
   4274 }
   4275 
   4276 - (DOMCSSStyleDeclaration *)_emptyStyle
   4277 {
   4278     return [[[self _frame] DOMDocument] createCSSStyleDeclaration];
   4279 }
   4280 
   4281 - (NSString *)_colorAsString:(NSColor *)color
   4282 {
   4283     NSColor *rgbColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
   4284     // FIXME: If color is non-nil and rgbColor is nil, that means we got some kind
   4285     // of fancy color that can't be converted to RGB. Changing that to "transparent"
   4286     // might not be great, but it's probably OK.
   4287     if (rgbColor == nil)
   4288         return @"transparent";
   4289     float r = [rgbColor redComponent];
   4290     float g = [rgbColor greenComponent];
   4291     float b = [rgbColor blueComponent];
   4292     float a = [rgbColor alphaComponent];
   4293     if (a == 0)
   4294         return @"transparent";
   4295     if (r == 0 && g == 0 && b == 0 && a == 1)
   4296         return @"black";
   4297     if (r == 1 && g == 1 && b == 1 && a == 1)
   4298         return @"white";
   4299     // FIXME: Lots more named colors. Maybe we could use the table in WebCore?
   4300     if (a == 1)
   4301         return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255];
   4302     return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a];
   4303 }
   4304 
   4305 - (NSString *)_shadowAsString:(NSShadow *)shadow
   4306 {
   4307     if (shadow == nil)
   4308         return @"none";
   4309     NSSize offset = [shadow shadowOffset];
   4310     float blurRadius = [shadow shadowBlurRadius];
   4311     if (offset.width == 0 && offset.height == 0 && blurRadius == 0)
   4312         return @"none";
   4313     NSColor *color = [shadow shadowColor];
   4314     if (color == nil)
   4315         return @"none";
   4316     // FIXME: Handle non-integral values here?
   4317     if (blurRadius == 0)
   4318         return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height];
   4319     return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius];
   4320 }
   4321 
   4322 - (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary
   4323 {
   4324     DOMCSSStyleDeclaration *style = [self _emptyStyle];
   4325 
   4326     NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName];
   4327     [style setBackgroundColor:[self _colorAsString:color]];
   4328 
   4329     NSFont *font = [dictionary objectForKey:NSFontAttributeName];
   4330     if (!font) {
   4331         [style setFontFamily:@"Helvetica"];
   4332         [style setFontSize:@"12px"];
   4333         [style setFontWeight:@"normal"];
   4334         [style setFontStyle:@"normal"];
   4335     } else {
   4336         NSFontManager *fm = [NSFontManager sharedFontManager];
   4337         // FIXME: Need more sophisticated escaping code if we want to handle family names
   4338         // with characters like single quote or backslash in their names.
   4339         [style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]];
   4340         [style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]];
   4341         // FIXME: Map to the entire range of CSS weight values.
   4342         if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT)
   4343             [style setFontWeight:@"bold"];
   4344         else
   4345             [style setFontWeight:@"normal"];
   4346         if ([fm traitsOfFont:font] & NSItalicFontMask)
   4347             [style setFontStyle:@"italic"];
   4348         else
   4349             [style setFontStyle:@"normal"];
   4350     }
   4351 
   4352     color = [dictionary objectForKey:NSForegroundColorAttributeName];
   4353     [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"];
   4354 
   4355     NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName];
   4356     [style setTextShadow:[self _shadowAsString:shadow]];
   4357 
   4358     int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue];
   4359 
   4360     int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue];
   4361     if (superscriptInt > 0)
   4362         [style setVerticalAlign:@"super"];
   4363     else if (superscriptInt < 0)
   4364         [style setVerticalAlign:@"sub"];
   4365     else
   4366         [style setVerticalAlign:@"baseline"];
   4367     int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue];
   4368     // FIXME: Underline wins here if we have both (see bug 3790443).
   4369     if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone)
   4370         [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
   4371     else if (underlineInt == NSUnderlineStyleNone)
   4372         [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
   4373     else
   4374         [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
   4375 
   4376     return style;
   4377 }
   4378 
   4379 - (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
   4380 {
   4381     if (Frame* coreFrame = core([self _frame]))
   4382         coreFrame->editor()->applyStyleToSelection(core(style), undoAction);
   4383 }
   4384 
   4385 - (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
   4386 {
   4387     if (Frame* coreFrame = core([self _frame]))
   4388         coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction);
   4389 }
   4390 
   4391 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
   4392 {
   4393     WebView *webView = [self _webView];
   4394     if (!webView)
   4395         return NO;
   4396 
   4397     if (![[webView preferences] respectStandardStyleKeyEquivalents])
   4398         return NO;
   4399 
   4400     if (![self _canEdit])
   4401         return NO;
   4402 
   4403     if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
   4404         return NO;
   4405 
   4406     NSString *string = [event characters];
   4407     if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
   4408         [self executeCoreCommandByName:"ToggleBold"];
   4409         return YES;
   4410     }
   4411     if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
   4412         [self executeCoreCommandByName:"ToggleItalic"];
   4413         return YES;
   4414     }
   4415 
   4416     return NO;
   4417 }
   4418 
   4419 - (BOOL)performKeyEquivalent:(NSEvent *)event
   4420 {
   4421     // There's a chance that responding to this event will run a nested event loop, and
   4422     // fetching a new event might release the old one. Retaining and then autoreleasing
   4423     // the current event prevents that from causing a problem inside WebKit or AppKit code.
   4424     [[event retain] autorelease];
   4425 
   4426     BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
   4427     BOOL ret = NO;
   4428 
   4429     [_private->keyDownEvent release];
   4430     _private->keyDownEvent = [event retain];
   4431 
   4432     [self retain];
   4433 
   4434     // Pass command-key combos through WebCore if there is a key binding available for
   4435     // this event. This lets web pages have a crack at intercepting command-modified keypresses.
   4436     // But don't do it if we have already handled the event.
   4437     // Pressing Esc results in a fake event being sent - don't pass it to WebCore.
   4438     if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder])
   4439         if (Frame* frame = core([self _frame]))
   4440             ret = frame->eventHandler()->keyEvent(event);
   4441 
   4442     if (!ret)
   4443         ret = [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event];
   4444 
   4445     [self release];
   4446 
   4447     return ret;
   4448 }
   4449 
   4450 - (void)copyFont:(id)sender
   4451 {
   4452     COMMAND_PROLOGUE
   4453 
   4454     // Put RTF with font attributes on the pasteboard.
   4455     // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
   4456     NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
   4457     [fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil];
   4458     [fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType];
   4459 }
   4460 
   4461 - (void)pasteFont:(id)sender
   4462 {
   4463     COMMAND_PROLOGUE
   4464 
   4465     // Read RTF with font attributes from the pasteboard.
   4466     // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
   4467     [self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:EditActionPasteFont];
   4468 }
   4469 
   4470 - (void)pasteAsRichText:(id)sender
   4471 {
   4472     COMMAND_PROLOGUE
   4473 
   4474     // Since rich text always beats plain text when both are on the pasteboard, it's not
   4475     // clear how this is different from plain old paste.
   4476     [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO];
   4477 }
   4478 
   4479 - (NSFont *)_originalFontA
   4480 {
   4481     return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f];
   4482 }
   4483 
   4484 - (NSFont *)_originalFontB
   4485 {
   4486     return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:NSFontItalicTrait weight:STANDARD_BOLD_WEIGHT size:12.0f];
   4487 }
   4488 
   4489 - (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b
   4490 {
   4491     // Since there's no way to directly ask NSFontManager what style change it's going to do
   4492     // we instead pass two "specimen" fonts to it and let it change them. We then deduce what
   4493     // style change it was doing by looking at what happened to each of the two fonts.
   4494     // So if it was making the text bold, both fonts will be bold after the fact.
   4495 
   4496     if (a == nil || b == nil)
   4497         return;
   4498 
   4499     NSFontManager *fm = [NSFontManager sharedFontManager];
   4500 
   4501     NSFont *oa = [self _originalFontA];
   4502 
   4503     NSString *aFamilyName = [a familyName];
   4504     NSString *bFamilyName = [b familyName];
   4505 
   4506     int aPointSize = (int)[a pointSize];
   4507     int bPointSize = (int)[b pointSize];
   4508 
   4509     int aWeight = [fm weightOfFont:a];
   4510     int bWeight = [fm weightOfFont:b];
   4511 
   4512     BOOL aIsItalic = ([fm traitsOfFont:a] & NSItalicFontMask) != 0;
   4513     BOOL bIsItalic = ([fm traitsOfFont:b] & NSItalicFontMask) != 0;
   4514 
   4515     BOOL aIsBold = aWeight > MIN_BOLD_WEIGHT;
   4516 
   4517     if ([aFamilyName isEqualToString:bFamilyName]) {
   4518         NSString *familyNameForCSS = aFamilyName;
   4519 
   4520         // The family name may not be specific enough to get us the font specified.
   4521         // In some cases, the only way to get exactly what we are looking for is to use
   4522         // the Postscript name.
   4523 
   4524         // Find the font the same way the rendering code would later if it encountered this CSS.
   4525         NSFontTraitMask traits = aIsItalic ? NSFontItalicTrait : 0;
   4526         int weight = aIsBold ? STANDARD_BOLD_WEIGHT : STANDARD_WEIGHT;
   4527         NSFont *foundFont = [WebFontCache fontWithFamily:aFamilyName traits:traits weight:weight size:aPointSize];
   4528 
   4529         // If we don't find a font with the same Postscript name, then we'll have to use the
   4530         // Postscript name to make the CSS specific enough.
   4531         if (![[foundFont fontName] isEqualToString:[a fontName]])
   4532             familyNameForCSS = [a fontName];
   4533 
   4534         // FIXME: Need more sophisticated escaping code if we want to handle family names
   4535         // with characters like single quote or backslash in their names.
   4536         [style setFontFamily:[NSString stringWithFormat:@"'%@'", familyNameForCSS]];
   4537     }
   4538 
   4539     int soa = (int)[oa pointSize];
   4540     if (aPointSize == bPointSize)
   4541         [style setFontSize:[NSString stringWithFormat:@"%dpx", aPointSize]];
   4542     else if (aPointSize < soa)
   4543         [style _setFontSizeDelta:@"-1px"];
   4544     else if (aPointSize > soa)
   4545         [style _setFontSizeDelta:@"1px"];
   4546 
   4547     // FIXME: Map to the entire range of CSS weight values.
   4548     if (aWeight == bWeight)
   4549         [style setFontWeight:aIsBold ? @"bold" : @"normal"];
   4550 
   4551     if (aIsItalic == bIsItalic)
   4552         [style setFontStyle:aIsItalic ? @"italic" :  @"normal"];
   4553 }
   4554 
   4555 - (DOMCSSStyleDeclaration *)_styleFromFontManagerOperation
   4556 {
   4557     DOMCSSStyleDeclaration *style = [self _emptyStyle];
   4558 
   4559     NSFontManager *fm = [NSFontManager sharedFontManager];
   4560 
   4561     NSFont *oa = [self _originalFontA];
   4562     NSFont *ob = [self _originalFontB];
   4563     [self _addToStyle:style fontA:[fm convertFont:oa] fontB:[fm convertFont:ob]];
   4564 
   4565     return style;
   4566 }
   4567 
   4568 - (void)changeFont:(id)sender
   4569 {
   4570     COMMAND_PROLOGUE
   4571 
   4572     [self _applyStyleToSelection:[self _styleFromFontManagerOperation] withUndoAction:EditActionSetFont];
   4573 }
   4574 
   4575 - (DOMCSSStyleDeclaration *)_styleForAttributeChange:(id)sender
   4576 {
   4577     DOMCSSStyleDeclaration *style = [self _emptyStyle];
   4578 
   4579     NSShadow *shadow = [[NSShadow alloc] init];
   4580     [shadow setShadowOffset:NSMakeSize(1, 1)];
   4581 
   4582     NSDictionary *oa = [NSDictionary dictionaryWithObjectsAndKeys:
   4583         [self _originalFontA], NSFontAttributeName,
   4584         nil];
   4585     NSDictionary *ob = [NSDictionary dictionaryWithObjectsAndKeys:
   4586         [NSColor blackColor], NSBackgroundColorAttributeName,
   4587         [self _originalFontB], NSFontAttributeName,
   4588         [NSColor whiteColor], NSForegroundColorAttributeName,
   4589         shadow, NSShadowAttributeName,
   4590         [NSNumber numberWithInt:NSUnderlineStyleSingle], NSStrikethroughStyleAttributeName,
   4591         [NSNumber numberWithInt:1], NSSuperscriptAttributeName,
   4592         [NSNumber numberWithInt:NSUnderlineStyleSingle], NSUnderlineStyleAttributeName,
   4593         nil];
   4594 
   4595     [shadow release];
   4596 
   4597 #if 0
   4598 
   4599 NSObliquenessAttributeName        /* float; skew to be applied to glyphs, default 0: no skew */
   4600     // font-style, but that is just an on-off switch
   4601 
   4602 NSExpansionAttributeName          /* float; log of expansion factor to be applied to glyphs, default 0: no expansion */
   4603     // font-stretch?
   4604 
   4605 NSKernAttributeName               /* float, amount to modify default kerning, if 0, kerning off */
   4606     // letter-spacing? probably not good enough
   4607 
   4608 NSUnderlineColorAttributeName     /* NSColor, default nil: same as foreground color */
   4609 NSStrikethroughColorAttributeName /* NSColor, default nil: same as foreground color */
   4610     // text-decoration-color?
   4611 
   4612 NSLigatureAttributeName           /* int, default 1: default ligatures, 0: no ligatures, 2: all ligatures */
   4613 NSBaselineOffsetAttributeName     /* float, in points; offset from baseline, default 0 */
   4614 NSStrokeWidthAttributeName        /* float, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0) */
   4615 NSStrokeColorAttributeName        /* NSColor, default nil: same as foreground color */
   4616     // need extensions?
   4617 
   4618 #endif
   4619 
   4620     NSDictionary *a = [sender convertAttributes:oa];
   4621     NSDictionary *b = [sender convertAttributes:ob];
   4622 
   4623     NSColor *ca = [a objectForKey:NSBackgroundColorAttributeName];
   4624     NSColor *cb = [b objectForKey:NSBackgroundColorAttributeName];
   4625     if (ca == cb) {
   4626         [style setBackgroundColor:[self _colorAsString:ca]];
   4627     }
   4628 
   4629     [self _addToStyle:style fontA:[a objectForKey:NSFontAttributeName] fontB:[b objectForKey:NSFontAttributeName]];
   4630 
   4631     ca = [a objectForKey:NSForegroundColorAttributeName];
   4632     cb = [b objectForKey:NSForegroundColorAttributeName];
   4633     if (ca == cb) {
   4634         [style setColor:[self _colorAsString:ca]];
   4635     }
   4636 
   4637     NSShadow *sha = [a objectForKey:NSShadowAttributeName];
   4638     if (sha)
   4639         [style setTextShadow:[self _shadowAsString:sha]];
   4640     else if ([b objectForKey:NSShadowAttributeName] == nil)
   4641         [style setTextShadow:@"none"];
   4642 
   4643     int sa = [[a objectForKey:NSStrikethroughStyleAttributeName] intValue];
   4644     int sb = [[b objectForKey:NSStrikethroughStyleAttributeName] intValue];
   4645     if (sa == sb) {
   4646         if (sa == NSUnderlineStyleNone)
   4647             [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
   4648             // we really mean "no line-through" rather than "none"
   4649         else
   4650             [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
   4651             // we really mean "add line-through" rather than "line-through"
   4652     }
   4653 
   4654     sa = [[a objectForKey:NSSuperscriptAttributeName] intValue];
   4655     sb = [[b objectForKey:NSSuperscriptAttributeName] intValue];
   4656     if (sa == sb) {
   4657         if (sa > 0)
   4658             [style setVerticalAlign:@"super"];
   4659         else if (sa < 0)
   4660             [style setVerticalAlign:@"sub"];
   4661         else
   4662             [style setVerticalAlign:@"baseline"];
   4663     }
   4664 
   4665     int ua = [[a objectForKey:NSUnderlineStyleAttributeName] intValue];
   4666     int ub = [[b objectForKey:NSUnderlineStyleAttributeName] intValue];
   4667     if (ua == ub) {
   4668         if (ua == NSUnderlineStyleNone)
   4669             [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
   4670             // we really mean "no underline" rather than "none"
   4671         else
   4672             [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
   4673             // we really mean "add underline" rather than "underline"
   4674     }
   4675 
   4676     return style;
   4677 }
   4678 
   4679 - (void)changeAttributes:(id)sender
   4680 {
   4681     COMMAND_PROLOGUE
   4682 
   4683     [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes];
   4684 }
   4685 
   4686 - (DOMCSSStyleDeclaration *)_styleFromColorPanelWithSelector:(SEL)selector
   4687 {
   4688     DOMCSSStyleDeclaration *style = [self _emptyStyle];
   4689 
   4690     ASSERT([style respondsToSelector:selector]);
   4691     [style performSelector:selector withObject:[self _colorAsString:[[NSColorPanel sharedColorPanel] color]]];
   4692 
   4693     return style;
   4694 }
   4695 
   4696 - (EditAction)_undoActionFromColorPanelWithSelector:(SEL)selector
   4697 {
   4698     if (selector == @selector(setBackgroundColor:))
   4699         return EditActionSetBackgroundColor;
   4700     return EditActionSetColor;
   4701 }
   4702 
   4703 - (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range
   4704 {
   4705     DOMCSSStyleDeclaration *style = [self _styleFromColorPanelWithSelector:selector];
   4706     WebView *webView = [self _webView];
   4707     if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:range])
   4708         if (Frame* coreFrame = core([self _frame]))
   4709             coreFrame->editor()->applyStyle(core(style), [self _undoActionFromColorPanelWithSelector:selector]);
   4710 }
   4711 
   4712 - (void)changeDocumentBackgroundColor:(id)sender
   4713 {
   4714     COMMAND_PROLOGUE
   4715 
   4716     // Mimicking NSTextView, this method sets the background color for the
   4717     // entire document. There is no NSTextView API for setting the background
   4718     // color on the selected range only. Note that this method is currently
   4719     // never called from the UI (see comment in changeColor:).
   4720     // FIXME: this actually has no effect when called, probably due to 3654850. _documentRange seems
   4721     // to do the right thing because it works in startSpeaking:, and I know setBackgroundColor: does the
   4722     // right thing because I tested it with [self _selectedRange].
   4723     // FIXME: This won't actually apply the style to the entire range here, because it ends up calling
   4724     // [frame _applyStyle:], which operates on the current selection. To make this work right, we'll
   4725     // need to save off the selection, temporarily set it to the entire range, make the change, then
   4726     // restore the old selection.
   4727     [self _changeCSSColorUsingSelector:@selector(setBackgroundColor:) inRange:[self _documentRange]];
   4728 }
   4729 
   4730 - (void)changeColor:(id)sender
   4731 {
   4732     COMMAND_PROLOGUE
   4733 
   4734     // FIXME: in NSTextView, this method calls changeDocumentBackgroundColor: when a
   4735     // private call has earlier been made by [NSFontFontEffectsBox changeColor:], see 3674493.
   4736     // AppKit will have to be revised to allow this to work with anything that isn't an
   4737     // NSTextView. However, this might not be required for Tiger, since the background-color
   4738     // changing box in the font panel doesn't work in Mail (3674481), though it does in TextEdit.
   4739     [self _applyStyleToSelection:[self _styleFromColorPanelWithSelector:@selector(setColor:)]
   4740                   withUndoAction:EditActionSetColor];
   4741 }
   4742 
   4743 - (void)_changeWordCaseWithSelector:(SEL)selector
   4744 {
   4745     if (![self _canEdit])
   4746         return;
   4747 
   4748     WebFrame *frame = [self _frame];
   4749     [self selectWord:nil];
   4750     NSString *word = [[frame _selectedString] performSelector:selector];
   4751     // FIXME: Does this need a different action context other than "typed"?
   4752     if ([self _shouldReplaceSelectionWithText:word givenAction:WebViewInsertActionTyped])
   4753         [frame _replaceSelectionWithText:word selectReplacement:NO smartReplace:NO];
   4754 }
   4755 
   4756 - (void)uppercaseWord:(id)sender
   4757 {
   4758     COMMAND_PROLOGUE
   4759 
   4760     [self _changeWordCaseWithSelector:@selector(uppercaseString)];
   4761 }
   4762 
   4763 - (void)lowercaseWord:(id)sender
   4764 {
   4765     COMMAND_PROLOGUE
   4766 
   4767     [self _changeWordCaseWithSelector:@selector(lowercaseString)];
   4768 }
   4769 
   4770 - (void)capitalizeWord:(id)sender
   4771 {
   4772     COMMAND_PROLOGUE
   4773 
   4774     [self _changeWordCaseWithSelector:@selector(capitalizedString)];
   4775 }
   4776 
   4777 - (void)complete:(id)sender
   4778 {
   4779     COMMAND_PROLOGUE
   4780 
   4781     if (![self _canEdit])
   4782         return;
   4783     if (!_private->completionController)
   4784         _private->completionController = [[WebTextCompletionController alloc] initWithWebView:[self _webView] HTMLView:self];
   4785     [_private->completionController doCompletion];
   4786 }
   4787 
   4788 - (void)checkSpelling:(id)sender
   4789 {
   4790     COMMAND_PROLOGUE
   4791 
   4792     if (Frame* coreFrame = core([self _frame]))
   4793         coreFrame->editor()->advanceToNextMisspelling();
   4794 }
   4795 
   4796 - (void)showGuessPanel:(id)sender
   4797 {
   4798     COMMAND_PROLOGUE
   4799 
   4800     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
   4801     if (!checker) {
   4802         LOG_ERROR("No NSSpellChecker");
   4803         return;
   4804     }
   4805 
   4806     NSPanel *spellingPanel = [checker spellingPanel];
   4807 #ifndef BUILDING_ON_TIGER
   4808     // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone
   4809     // to match rest of OS X.
   4810     if ([spellingPanel isVisible]) {
   4811         [spellingPanel orderOut:sender];
   4812         return;
   4813     }
   4814 #endif
   4815 
   4816     if (Frame* coreFrame = core([self _frame]))
   4817         coreFrame->editor()->advanceToNextMisspelling(true);
   4818     [spellingPanel orderFront:sender];
   4819 }
   4820 
   4821 - (void)_changeSpellingToWord:(NSString *)newWord
   4822 {
   4823     if (![self _canEdit])
   4824         return;
   4825 
   4826     // Don't correct to empty string.  (AppKit checked this, we might as well too.)
   4827     if (![NSSpellChecker sharedSpellChecker]) {
   4828         LOG_ERROR("No NSSpellChecker");
   4829         return;
   4830     }
   4831 
   4832     if ([newWord isEqualToString:@""])
   4833         return;
   4834 
   4835     if ([self _shouldReplaceSelectionWithText:newWord givenAction:WebViewInsertActionPasted])
   4836         [[self _frame] _replaceSelectionWithText:newWord selectReplacement:YES smartReplace:NO];
   4837 }
   4838 
   4839 - (void)changeSpelling:(id)sender
   4840 {
   4841     COMMAND_PROLOGUE
   4842 
   4843     [self _changeSpellingToWord:[[sender selectedCell] stringValue]];
   4844 }
   4845 
   4846 - (void)performFindPanelAction:(id)sender
   4847 {
   4848     COMMAND_PROLOGUE
   4849 
   4850     // Implementing this will probably require copying all of NSFindPanel.h and .m.
   4851     // We need *almost* the same thing as AppKit, but not quite.
   4852     LOG_ERROR("unimplemented");
   4853 }
   4854 
   4855 - (void)startSpeaking:(id)sender
   4856 {
   4857     COMMAND_PROLOGUE
   4858 
   4859     WebFrame *frame = [self _frame];
   4860     DOMRange *range = [self _selectedRange];
   4861     if (!range || [range collapsed])
   4862         range = [self _documentRange];
   4863     [NSApp speakString:[frame _stringForRange:range]];
   4864 }
   4865 
   4866 - (void)stopSpeaking:(id)sender
   4867 {
   4868     COMMAND_PROLOGUE
   4869 
   4870     [NSApp stopSpeaking:sender];
   4871 }
   4872 
   4873 - (void)toggleBaseWritingDirection:(id)sender
   4874 {
   4875     COMMAND_PROLOGUE
   4876 
   4877     if (![self _canEdit])
   4878         return;
   4879 
   4880     Frame* coreFrame = core([self _frame]);
   4881     if (!coreFrame)
   4882         return;
   4883 
   4884     WritingDirection direction = RightToLeftWritingDirection;
   4885     switch (coreFrame->editor()->baseWritingDirectionForSelectionStart()) {
   4886         case NSWritingDirectionLeftToRight:
   4887             break;
   4888         case NSWritingDirectionRightToLeft:
   4889             direction = LeftToRightWritingDirection;
   4890             break;
   4891         // The writingDirectionForSelectionStart method will never return "natural". It
   4892         // will always return a concrete direction. So, keep the compiler happy, and assert not reached.
   4893         case NSWritingDirectionNatural:
   4894             ASSERT_NOT_REACHED();
   4895             break;
   4896     }
   4897 
   4898     if (Frame* coreFrame = core([self _frame]))
   4899         coreFrame->editor()->setBaseWritingDirection(direction);
   4900 }
   4901 
   4902 - (void)changeBaseWritingDirection:(id)sender
   4903 {
   4904     COMMAND_PROLOGUE
   4905 
   4906     if (![self _canEdit])
   4907         return;
   4908 
   4909     NSWritingDirection writingDirection = static_cast<NSWritingDirection>([sender tag]);
   4910 
   4911     // We disable the menu item that performs this action because we can't implement
   4912     // NSWritingDirectionNatural's behavior using CSS.
   4913     ASSERT(writingDirection != NSWritingDirectionNatural);
   4914 
   4915     if (Frame* coreFrame = core([self _frame]))
   4916         coreFrame->editor()->setBaseWritingDirection(writingDirection == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection);
   4917 }
   4918 
   4919 static BOOL writingDirectionKeyBindingsEnabled()
   4920 {
   4921 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
   4922     return YES;
   4923 #else
   4924     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   4925     return [defaults boolForKey:@"NSAllowsBaseWritingDirectionKeyBindings"] || [defaults boolForKey:@"AppleTextDirection"];
   4926 #endif
   4927 }
   4928 
   4929 - (void)_changeBaseWritingDirectionTo:(NSWritingDirection)direction
   4930 {
   4931     if (![self _canEdit])
   4932         return;
   4933 
   4934     static BOOL bindingsEnabled = writingDirectionKeyBindingsEnabled();
   4935 
   4936     if (!bindingsEnabled) {
   4937         NSBeep();
   4938         return;
   4939     }
   4940 
   4941     if (Frame* coreFrame = core([self _frame]))
   4942         coreFrame->editor()->setBaseWritingDirection(direction == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection);
   4943 }
   4944 
   4945 - (void)makeBaseWritingDirectionLeftToRight:(id)sender
   4946 {
   4947     COMMAND_PROLOGUE
   4948 
   4949     [self _changeBaseWritingDirectionTo:NSWritingDirectionLeftToRight];
   4950 }
   4951 
   4952 - (void)makeBaseWritingDirectionRightToLeft:(id)sender
   4953 {
   4954     COMMAND_PROLOGUE
   4955 
   4956     [self _changeBaseWritingDirectionTo:NSWritingDirectionRightToLeft];
   4957 }
   4958 
   4959 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
   4960 - (void)changeBaseWritingDirectionToLTR:(id)sender
   4961 {
   4962     [self makeBaseWritingDirectionLeftToRight:sender];
   4963 }
   4964 
   4965 - (void)changeBaseWritingDirectionToRTL:(id)sender
   4966 {
   4967     [self makeBaseWritingDirectionRightToLeft:sender];
   4968 }
   4969 #endif
   4970 
   4971 - (void)makeBaseWritingDirectionNatural:(id)sender
   4972 {
   4973     LOG_ERROR("Sent from %@.", sender);
   4974 }
   4975 
   4976 #if 0
   4977 
   4978 // CSS does not have a way to specify an outline font, which may make this difficult to implement.
   4979 // Maybe a special case of text-shadow?
   4980 - (void)outline:(id)sender;
   4981 
   4982 // This is part of table support, which may be in NSTextView for Tiger.
   4983 // It's probably simple to do the equivalent thing for WebKit.
   4984 - (void)insertTable:(id)sender;
   4985 
   4986 // This could be important.
   4987 - (void)toggleTraditionalCharacterShape:(id)sender;
   4988 
   4989 // I'm not sure what the equivalents of these in the web world are.
   4990 - (void)insertLineSeparator:(id)sender;
   4991 - (void)insertPageBreak:(id)sender;
   4992 
   4993 // These methods are not implemented in NSTextView yet at the time of this writing.
   4994 - (void)changeCaseOfLetter:(id)sender;
   4995 - (void)transposeWords:(id)sender;
   4996 
   4997 #endif
   4998 
   4999 #ifndef BUILDING_ON_TIGER
   5000 
   5001 // Override this so that AppKit will send us arrow keys as key down events so we can
   5002 // support them via the key bindings mechanism.
   5003 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
   5004 {
   5005     bool haveWebCoreFrame = core([self _frame]);
   5006 
   5007     // If we have a frame, our keyDown method will handle key bindings after sending
   5008     // the event through the DOM, so ask AppKit not to do its early special key binding
   5009     // mapping. If we don't have a frame, just let things work the normal way without
   5010     // a keyDown.
   5011     return haveWebCoreFrame;
   5012 }
   5013 
   5014 #else
   5015 
   5016 // Super-hack alert.
   5017 // All this code accomplishes the same thing as the _wantsKeyDownForEvent method above.
   5018 
   5019 // Returns a selector only if called while:
   5020 //   1) first responder is self
   5021 //   2) handling a key down event
   5022 //   3) not yet inside keyDown: method
   5023 //   4) key is an arrow key
   5024 // The selector is the one that gets sent by -[NSWindow _processKeyboardUIKey] for this key.
   5025 - (SEL)_arrowKeyDownEventSelectorIfPreprocessing
   5026 {
   5027     NSWindow *w = [self window];
   5028     if ([w firstResponder] != self)
   5029         return NULL;
   5030     NSEvent *e = [w currentEvent];
   5031     if ([e type] != NSKeyDown)
   5032         return NULL;
   5033     if (e == _private->keyDownEvent)
   5034         return NULL;
   5035     NSString *s = [e charactersIgnoringModifiers];
   5036     if ([s length] == 0)
   5037         return NULL;
   5038     switch ([s characterAtIndex:0]) {
   5039         case NSDownArrowFunctionKey:
   5040             return @selector(moveDown:);
   5041         case NSLeftArrowFunctionKey:
   5042             return @selector(moveLeft:);
   5043         case NSRightArrowFunctionKey:
   5044             return @selector(moveRight:);
   5045         case NSUpArrowFunctionKey:
   5046             return @selector(moveUp:);
   5047         default:
   5048             return NULL;
   5049     }
   5050 }
   5051 
   5052 // Returns NO instead of YES if called on the selector that the
   5053 // _arrowKeyDownEventSelectorIfPreprocessing method returns.
   5054 // This should only happen inside -[NSWindow _processKeyboardUIKey],
   5055 // and together with the change below should cause that method
   5056 // to return NO rather than handling the key.
   5057 // Also set a 1-shot flag for the nextResponder check below.
   5058 - (BOOL)respondsToSelector:(SEL)selector
   5059 {
   5060     if (![super respondsToSelector:selector])
   5061         return NO;
   5062     SEL arrowKeySelector = [self _arrowKeyDownEventSelectorIfPreprocessing];
   5063     if (selector != arrowKeySelector)
   5064         return YES;
   5065     _private->nextResponderDisabledOnce = YES;
   5066     return NO;
   5067 }
   5068 
   5069 // Returns nil instead of the next responder if called when the
   5070 // one-shot flag is set, and _arrowKeyDownEventSelectorIfPreprocessing
   5071 // returns something other than NULL. This should only happen inside
   5072 // -[NSWindow _processKeyboardUIKey] and together with the change above
   5073 // should cause that method to return NO rather than handling the key.
   5074 - (NSResponder *)nextResponder
   5075 {
   5076     BOOL disabled = _private->nextResponderDisabledOnce;
   5077     _private->nextResponderDisabledOnce = NO;
   5078     if (disabled && [self _arrowKeyDownEventSelectorIfPreprocessing] != NULL)
   5079         return nil;
   5080     return [super nextResponder];
   5081 }
   5082 
   5083 #endif
   5084 
   5085 - (void)_updateControlTints
   5086 {
   5087     Frame* frame = core([self _frame]);
   5088     if (!frame)
   5089         return;
   5090     FrameView* view = frame->view();
   5091     if (!view)
   5092         return;
   5093     view->updateControlTints();
   5094 }
   5095 
   5096 // Despite its name, this is called at different times than windowDidBecomeKey is.
   5097 // It takes into account all the other factors that determine when NSCell draws
   5098 // with different tints, so it's the right call to use for control tints. We'd prefer
   5099 // to do this with API. <rdar://problem/5136760>
   5100 - (void)_windowChangedKeyState
   5101 {
   5102     if (pthread_main_np())
   5103         [self _updateControlTints];
   5104     else
   5105         [self performSelectorOnMainThread:@selector(_updateControlTints) withObject:nil waitUntilDone:NO];
   5106 
   5107     [super _windowChangedKeyState];
   5108 }
   5109 
   5110 - (void)otherMouseDown:(NSEvent *)event
   5111 {
   5112     if ([event buttonNumber] == 2)
   5113         [self mouseDown:event];
   5114     else
   5115         [super otherMouseDown:event];
   5116 }
   5117 
   5118 - (void)otherMouseDragged:(NSEvent *)event
   5119 {
   5120     if ([event buttonNumber] == 2)
   5121         [self mouseDragged:event];
   5122     else
   5123         [super otherMouseDragged:event];
   5124 }
   5125 
   5126 - (void)otherMouseUp:(NSEvent *)event
   5127 {
   5128     if ([event buttonNumber] == 2)
   5129         [self mouseUp:event];
   5130     else
   5131         [super otherMouseUp:event];
   5132 }
   5133 
   5134 @end
   5135 
   5136 @implementation WebHTMLView (WebInternal)
   5137 
   5138 - (void)_selectionChanged
   5139 {
   5140     [self _updateSelectionForInputManager];
   5141     [self _updateFontPanel];
   5142     if (Frame* coreFrame = core([self _frame]))
   5143         coreFrame->editor()->setStartNewKillRingSequence(true);
   5144 }
   5145 
   5146 - (void)_updateFontPanel
   5147 {
   5148     // FIXME: NSTextView bails out if becoming or resigning first responder, for which it has ivar flags. Not
   5149     // sure if we need to do something similar.
   5150 
   5151     if (![self _canEdit])
   5152         return;
   5153 
   5154     NSWindow *window = [self window];
   5155     // FIXME: is this first-responder check correct? What happens if a subframe is editable and is first responder?
   5156     if (![window isKeyWindow] || [window firstResponder] != self)
   5157         return;
   5158 
   5159     bool multipleFonts = false;
   5160     NSFont *font = nil;
   5161     if (Frame* coreFrame = core([self _frame])) {
   5162         if (const SimpleFontData* fd = coreFrame->editor()->fontForSelection(multipleFonts))
   5163             font = fd->getNSFont();
   5164     }
   5165 
   5166     // FIXME: for now, return a bogus font that distinguishes the empty selection from the non-empty
   5167     // selection. We should be able to remove this once the rest of this code works properly.
   5168     if (font == nil)
   5169         font = [self _hasSelection] ? [NSFont menuFontOfSize:23] : [NSFont toolTipsFontOfSize:17];
   5170     ASSERT(font != nil);
   5171 
   5172     [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:multipleFonts];
   5173 
   5174     // FIXME: we don't keep track of selected attributes, or set them on the font panel. This
   5175     // appears to have no effect on the UI. E.g., underlined text in Mail or TextEdit is
   5176     // not reflected in the font panel. Maybe someday this will change.
   5177 }
   5178 
   5179 - (BOOL)_canSmartCopyOrDelete
   5180 {
   5181     if (![[self _webView] smartInsertDeleteEnabled])
   5182         return NO;
   5183     Frame* coreFrame = core([self _frame]);
   5184     return coreFrame && coreFrame->selection()->granularity() == WordGranularity;
   5185 }
   5186 
   5187 - (NSEvent *)_mouseDownEvent
   5188 {
   5189     return _private->mouseDownEvent;
   5190 }
   5191 
   5192 - (id<WebHTMLHighlighter>)_highlighterForType:(NSString*)type
   5193 {
   5194     return [_private->highlighters objectForKey:type];
   5195 }
   5196 
   5197 - (WebFrame *)_frame
   5198 {
   5199     return [_private->dataSource webFrame];
   5200 }
   5201 
   5202 - (void)closeIfNotCurrentView
   5203 {
   5204     if ([[[self _frame] frameView] documentView] != self)
   5205         [self close];
   5206 }
   5207 
   5208 - (DOMDocumentFragment*)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
   5209 {
   5210     return [self _documentFragmentFromPasteboard:pasteboard inContext:nil allowPlainText:NO];
   5211 }
   5212 
   5213 #ifndef BUILDING_ON_TIGER
   5214 
   5215 - (BOOL)isGrammarCheckingEnabled
   5216 {
   5217     // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
   5218     // the AppKit code checks the first responder.
   5219     return [[self _webView] isGrammarCheckingEnabled];
   5220 }
   5221 
   5222 - (void)setGrammarCheckingEnabled:(BOOL)flag
   5223 {
   5224     // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
   5225     // the AppKit code checks the first responder.
   5226     [[self _webView] setGrammarCheckingEnabled:flag];
   5227 }
   5228 
   5229 - (void)toggleGrammarChecking:(id)sender
   5230 {
   5231     // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
   5232     // the AppKit code checks the first responder.
   5233     [[self _webView] toggleGrammarChecking:sender];
   5234 }
   5235 
   5236 
   5237 static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
   5238 {
   5239     NSArray *screens = [NSScreen screens];
   5240 
   5241     if ([screens count] == 0) {
   5242         // You could theoretically get here if running with no monitor, in which case it doesn't matter
   5243         // much where the "on-screen" point is.
   5244         return CGPointMake(point.x, point.y);
   5245     }
   5246 
   5247     // Flip the y coordinate from the top of the menu bar screen -- see 4636390
   5248     return CGPointMake(point.x, NSMaxY([[screens objectAtIndex:0] frame]) - point.y);
   5249 }
   5250 
   5251 #endif
   5252 
   5253 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
   5254 
   5255 - (void)orderFrontSubstitutionsPanel:(id)sender
   5256 {
   5257     COMMAND_PROLOGUE
   5258 
   5259     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
   5260     if (!checker) {
   5261         LOG_ERROR("No NSSpellChecker");
   5262         return;
   5263     }
   5264 
   5265     NSPanel *substitutionsPanel = [checker substitutionsPanel];
   5266     if ([substitutionsPanel isVisible]) {
   5267         [substitutionsPanel orderOut:sender];
   5268         return;
   5269     }
   5270     [substitutionsPanel orderFront:sender];
   5271 }
   5272 
   5273 // FIXME 4799134: WebView is the bottleneck for this logic, but we must implement these methods here because
   5274 // the AppKit code checks the first responder.
   5275 
   5276 - (BOOL)smartInsertDeleteEnabled
   5277 {
   5278     return [[self _webView] smartInsertDeleteEnabled];
   5279 }
   5280 
   5281 - (void)setSmartInsertDeleteEnabled:(BOOL)flag
   5282 {
   5283     [[self _webView] setSmartInsertDeleteEnabled:flag];
   5284 }
   5285 
   5286 - (void)toggleSmartInsertDelete:(id)sender
   5287 {
   5288     [[self _webView] toggleSmartInsertDelete:sender];
   5289 }
   5290 
   5291 - (BOOL)isAutomaticQuoteSubstitutionEnabled
   5292 {
   5293     return [[self _webView] isAutomaticQuoteSubstitutionEnabled];
   5294 }
   5295 
   5296 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
   5297 {
   5298     [[self _webView] setAutomaticQuoteSubstitutionEnabled:flag];
   5299 }
   5300 
   5301 - (void)toggleAutomaticQuoteSubstitution:(id)sender
   5302 {
   5303     [[self _webView] toggleAutomaticQuoteSubstitution:sender];
   5304 }
   5305 
   5306 - (BOOL)isAutomaticLinkDetectionEnabled
   5307 {
   5308     return [[self _webView] isAutomaticLinkDetectionEnabled];
   5309 }
   5310 
   5311 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
   5312 {
   5313     [[self _webView] setAutomaticLinkDetectionEnabled:flag];
   5314 }
   5315 
   5316 - (void)toggleAutomaticLinkDetection:(id)sender
   5317 {
   5318     [[self _webView] toggleAutomaticLinkDetection:sender];
   5319 }
   5320 
   5321 - (BOOL)isAutomaticDashSubstitutionEnabled
   5322 {
   5323     return [[self _webView] isAutomaticDashSubstitutionEnabled];
   5324 }
   5325 
   5326 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
   5327 {
   5328     [[self _webView] setAutomaticDashSubstitutionEnabled:flag];
   5329 }
   5330 
   5331 - (void)toggleAutomaticDashSubstitution:(id)sender
   5332 {
   5333     [[self _webView] toggleAutomaticDashSubstitution:sender];
   5334 }
   5335 
   5336 - (BOOL)isAutomaticTextReplacementEnabled
   5337 {
   5338     return [[self _webView] isAutomaticTextReplacementEnabled];
   5339 }
   5340 
   5341 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
   5342 {
   5343     [[self _webView] setAutomaticTextReplacementEnabled:flag];
   5344 }
   5345 
   5346 - (void)toggleAutomaticTextReplacement:(id)sender
   5347 {
   5348     [[self _webView] toggleAutomaticTextReplacement:sender];
   5349 }
   5350 
   5351 - (BOOL)isAutomaticSpellingCorrectionEnabled
   5352 {
   5353     return [[self _webView] isAutomaticSpellingCorrectionEnabled];
   5354 }
   5355 
   5356 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag
   5357 {
   5358     [[self _webView] setAutomaticSpellingCorrectionEnabled:flag];
   5359 }
   5360 
   5361 - (void)toggleAutomaticSpellingCorrection:(id)sender
   5362 {
   5363     [[self _webView] toggleAutomaticSpellingCorrection:sender];
   5364 }
   5365 
   5366 #endif
   5367 
   5368 - (void)_lookUpInDictionaryFromMenu:(id)sender
   5369 {
   5370     // Dictionary API will accept a whitespace-only string and display UI as if it were real text,
   5371     // so bail out early to avoid that.
   5372     if ([[[self selectedString] _webkit_stringByTrimmingWhitespace] length] == 0)
   5373         return;
   5374 
   5375     NSAttributedString *attrString = [self selectedAttributedString];
   5376 
   5377     Frame* coreFrame = core([self _frame]);
   5378     if (!coreFrame)
   5379         return;
   5380 
   5381     NSRect rect = coreFrame->selection()->bounds();
   5382 
   5383 #ifndef BUILDING_ON_TIGER
   5384     NSDictionary *attributes = [attrString fontAttributesInRange:NSMakeRange(0,1)];
   5385     NSFont *font = [attributes objectForKey:NSFontAttributeName];
   5386     if (font)
   5387         rect.origin.y += [font ascender];
   5388 #endif
   5389 
   5390 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
   5391     [self showDefinitionForAttributedString:attrString atPoint:rect.origin];
   5392     return;
   5393 #endif
   5394 
   5395     // We soft link to get the function that displays the dictionary (either pop-up window or app) to avoid the performance
   5396     // penalty of linking to another framework. This function changed signature as well as framework between Tiger and Leopard,
   5397     // so the two cases are handled separately.
   5398 
   5399 #ifdef BUILDING_ON_TIGER
   5400     typedef OSStatus (*ServiceWindowShowFunction)(id inWordString, NSRect inWordBoundary, UInt16 inLineDirection);
   5401     const char *frameworkPath = "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/LangAnalysis.framework/LangAnalysis";
   5402     const char *functionName = "DCMDictionaryServiceWindowShow";
   5403 #else
   5404     typedef void (*ServiceWindowShowFunction)(id unusedDictionaryRef, id inWordString, CFRange selectionRange, id unusedFont, CGPoint textOrigin, Boolean verticalText, id unusedTransform);
   5405     const char *frameworkPath = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
   5406     const char *functionName = "HIDictionaryWindowShow";
   5407 #endif
   5408 
   5409     static bool lookedForFunction = false;
   5410     static ServiceWindowShowFunction dictionaryServiceWindowShow = NULL;
   5411 
   5412     if (!lookedForFunction) {
   5413         void* langAnalysisFramework = dlopen(frameworkPath, RTLD_LAZY);
   5414         ASSERT(langAnalysisFramework);
   5415         if (langAnalysisFramework)
   5416             dictionaryServiceWindowShow = (ServiceWindowShowFunction)dlsym(langAnalysisFramework, functionName);
   5417         lookedForFunction = true;
   5418     }
   5419 
   5420     ASSERT(dictionaryServiceWindowShow);
   5421     if (!dictionaryServiceWindowShow) {
   5422         NSLog(@"Couldn't find the %s function in %s", functionName, frameworkPath);
   5423         return;
   5424     }
   5425 
   5426 #ifdef BUILDING_ON_TIGER
   5427     // FIXME: must check for right-to-left here
   5428     NSWritingDirection writingDirection = NSWritingDirectionLeftToRight;
   5429 
   5430     // FIXME: the dictionary API expects the rect for the first line of selection. Passing
   5431     // the rect for the entire selection, as we do here, positions the pop-up window near
   5432     // the bottom of the selection rather than at the selected word.
   5433     rect = [self convertRect:rect toView:nil];
   5434     rect.origin = [[self window] convertBaseToScreen:rect.origin];
   5435     NSData *data = [attrString RTFFromRange:NSMakeRange(0, [attrString length]) documentAttributes:nil];
   5436     dictionaryServiceWindowShow(data, rect, (writingDirection == NSWritingDirectionRightToLeft) ? 1 : 0);
   5437 #else
   5438     // The HIDictionaryWindowShow function requires the origin, in CG screen coordinates, of the first character of text in the selection.
   5439     // FIXME 4945808: We approximate this in a way that works well when a single word is selected, and less well in some other cases
   5440     // (but no worse than we did in Tiger)
   5441     NSPoint windowPoint = [self convertPoint:rect.origin toView:nil];
   5442     NSPoint screenPoint = [[self window] convertBaseToScreen:windowPoint];
   5443 
   5444     dictionaryServiceWindowShow(nil, attrString, CFRangeMake(0, [attrString length]), nil,
   5445                                 coreGraphicsScreenPointForAppKitScreenPoint(screenPoint), false, nil);
   5446 #endif
   5447 }
   5448 
   5449 - (void)_hoverFeedbackSuspendedChanged
   5450 {
   5451     [self _updateMouseoverWithFakeEvent];
   5452 }
   5453 
   5454 - (void)_executeSavedKeypressCommands
   5455 {
   5456     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
   5457     if (!parameters || parameters->event->keypressCommands().isEmpty())
   5458         return;
   5459 
   5460     // We could be called again if the execution of one command triggers a call to selectedRange.
   5461     // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result
   5462     if (parameters->executingSavedKeypressCommands)
   5463         return;
   5464 
   5465     // Avoid an infinite loop that would occur if executing a command appended it to event->keypressCommands() again.
   5466     bool wasSavingCommands = parameters->shouldSaveCommands;
   5467     parameters->shouldSaveCommands = false;
   5468     parameters->executingSavedKeypressCommands = true;
   5469 
   5470     const Vector<KeypressCommand>& commands = parameters->event->keypressCommands();
   5471 
   5472     for (size_t i = 0; i < commands.size(); ++i) {
   5473         if (commands[i].commandName == "insertText:")
   5474             [self insertText:commands[i].text];
   5475         else if (commands[i].commandName == "noop:")
   5476             ; // Do nothing. This case can be removed once <rdar://problem/9025012> is fixed.
   5477         else
   5478             [self doCommandBySelector:NSSelectorFromString(commands[i].commandName)];
   5479     }
   5480     parameters->event->keypressCommands().clear();
   5481     parameters->shouldSaveCommands = wasSavingCommands;
   5482     parameters->executingSavedKeypressCommands = false;
   5483 }
   5484 
   5485 - (BOOL)_interpretKeyEvent:(KeyboardEvent*)event savingCommands:(BOOL)savingCommands
   5486 {
   5487     ASSERT(core([self _frame]) == event->target()->toNode()->document()->frame());
   5488     ASSERT(!savingCommands || event->keypressCommands().isEmpty()); // Save commands once for each event.
   5489 
   5490     WebHTMLViewInterpretKeyEventsParameters parameters;
   5491     parameters.eventInterpretationHadSideEffects = false;
   5492     parameters.shouldSaveCommands = savingCommands;
   5493     parameters.executingSavedKeypressCommands = false;
   5494     // If we're intercepting the initial IM call we assume that the IM has consumed the event,
   5495     // and only change this assumption if one of the NSTextInput/Responder callbacks is used.
   5496     // We assume the IM will *not* consume hotkey sequences
   5497     parameters.consumedByIM = savingCommands && !event->metaKey();
   5498 
   5499     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
   5500     if (!platformEvent)
   5501         return NO;
   5502 
   5503     NSEvent *macEvent = platformEvent->macEvent();
   5504     if ([macEvent type] == NSKeyDown && [_private->completionController filterKeyDown:macEvent])
   5505         return YES;
   5506 
   5507     if ([macEvent type] == NSFlagsChanged)
   5508         return NO;
   5509 
   5510     parameters.event = event;
   5511     _private->interpretKeyEventsParameters = &parameters;
   5512     const Vector<KeypressCommand>& commands = event->keypressCommands();
   5513 
   5514     if (savingCommands) {
   5515         // AppKit will respond with a series of NSTextInput protocol method calls. There are three groups that we heuristically differentiate:
   5516         // 1. Key Bindings. Only doCommandBySelector: and insertText: calls will be made, which we save in the event for execution
   5517         // after DOM dispatch. This is safe, because neither returns a result, so there is no branching on AppKit side.
   5518         // 2. Plain text input. Here as well, we need to dispatch DOM events prior to inserting text, so we save the insertText: command.
   5519         // 3. Input method processing. An IM can make any NSTextInput calls, and can base its decisions on results it gets, so we must
   5520         // execute the calls immediately. DOM events like keydown are tweaked to have keyCode of 229, and canceling them has no effect.
   5521         // Unfortunately, there is no real difference between plain text input and IM processing - for example, AppKit queries hasMarkedText
   5522         // when typing with U.S. keyboard, and inserts marked text for dead keys.
   5523         [self interpretKeyEvents:[NSArray arrayWithObject:macEvent]];
   5524     } else {
   5525         // Are there commands that could just cause text insertion if executed via Editor?
   5526         // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
   5527         // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
   5528         // (e.g. Tab that inserts a Tab character, or Enter).
   5529         bool haveTextInsertionCommands = false;
   5530         for (size_t i = 0; i < commands.size(); ++i) {
   5531             if ([self coreCommandBySelector:NSSelectorFromString(commands[i].commandName)].isTextInsertion())
   5532                 haveTextInsertionCommands = true;
   5533         }
   5534         // If there are no text insertion commands, default keydown handler is the right time to execute the commands.
   5535         // Keypress (Char event) handler is the latest opportunity to execute.
   5536         if (!haveTextInsertionCommands || platformEvent->type() == PlatformKeyboardEvent::Char)
   5537             [self _executeSavedKeypressCommands];
   5538     }
   5539     _private->interpretKeyEventsParameters = 0;
   5540 
   5541     // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
   5542     // IM-like actions are handled immediately (so parameters.eventInterpretationHadSideEffects is true), but there are saved commands that
   5543     // should be handled like normal text input after DOM event dispatch.
   5544     if (!event->keypressCommands().isEmpty())
   5545         return NO;
   5546 
   5547     // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
   5548     // in which case we should not bubble the event up the DOM.
   5549     if (parameters.consumedByIM)
   5550         return YES;
   5551 
   5552     // If we have already executed all commands, don't do it again.
   5553     return parameters.eventInterpretationHadSideEffects;
   5554 }
   5555 
   5556 - (WebCore::CachedImage*)promisedDragTIFFDataSource
   5557 {
   5558     return _private->promisedDragTIFFDataSource;
   5559 }
   5560 
   5561 - (void)setPromisedDragTIFFDataSource:(WebCore::CachedImage*)source
   5562 {
   5563     if (source)
   5564         source->addClient(promisedDataClient());
   5565 
   5566     if (_private->promisedDragTIFFDataSource)
   5567         _private->promisedDragTIFFDataSource->removeClient(promisedDataClient());
   5568     _private->promisedDragTIFFDataSource = source;
   5569 }
   5570 
   5571 #undef COMMAND_PROLOGUE
   5572 
   5573 - (void)_layoutIfNeeded
   5574 {
   5575     ASSERT(!_private->subviewsSetAside);
   5576 
   5577     if ([self _needsLayout])
   5578         [self layout];
   5579 }
   5580 
   5581 - (void)_web_updateLayoutAndStyleIfNeededRecursive
   5582 {
   5583     WebFrame *webFrame = [self _frame];
   5584     Frame* coreFrame = core(webFrame);
   5585     if (coreFrame && coreFrame->view())
   5586         coreFrame->view()->updateLayoutAndStyleIfNeededRecursive();
   5587 }
   5588 
   5589 - (void) _destroyAllWebPlugins
   5590 {
   5591     [[self _pluginController] destroyAllPlugins];
   5592 }
   5593 
   5594 - (BOOL)_needsLayout
   5595 {
   5596     return [[self _frame] _needsLayout];
   5597 }
   5598 
   5599 #if USE(ACCELERATED_COMPOSITING)
   5600 - (void)attachRootLayer:(CALayer*)layer
   5601 {
   5602     if (!_private->layerHostingView) {
   5603         NSView* hostingView = [[NSView alloc] initWithFrame:[self bounds]];
   5604 #if !defined(BUILDING_ON_LEOPARD)
   5605         [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
   5606 #endif
   5607         [self addSubview:hostingView];
   5608         [hostingView release];
   5609         // hostingView is owned by being a subview of self
   5610         _private->layerHostingView = hostingView;
   5611     }
   5612 
   5613     // Make a container layer, which will get sized/positioned by AppKit and CA.
   5614     CALayer* viewLayer = [CALayer layer];
   5615 
   5616 #if defined(BUILDING_ON_LEOPARD)
   5617     // Turn off default animations.
   5618     NSNull *nullValue = [NSNull null];
   5619     NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys:
   5620                              nullValue, @"anchorPoint",
   5621                              nullValue, @"bounds",
   5622                              nullValue, @"contents",
   5623                              nullValue, @"contentsRect",
   5624                              nullValue, @"opacity",
   5625                              nullValue, @"position",
   5626                              nullValue, @"sublayerTransform",
   5627                              nullValue, @"sublayers",
   5628                              nullValue, @"transform",
   5629                              nil];
   5630     [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]];
   5631 #endif
   5632 
   5633 #if !defined(BUILDING_ON_LEOPARD)
   5634     // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale
   5635     // via -viewDidMoveToWindow.
   5636     NSWindow *window = [self window];
   5637     CGFloat scaleFactor;
   5638 #if !defined(BUILDING_ON_SNOW_LEOPARD)
   5639     if (window)
   5640         scaleFactor = [window backingScaleFactor];
   5641     else
   5642         scaleFactor = [[NSScreen mainScreen] backingScaleFactor];
   5643 #else
   5644     if (window)
   5645         scaleFactor = [window userSpaceScaleFactor];
   5646     else
   5647         scaleFactor = [[NSScreen mainScreen] userSpaceScaleFactor];
   5648 #endif
   5649 
   5650     [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)];
   5651 #endif
   5652 
   5653     if ([self layer]) {
   5654         // If we are in a layer-backed view, we need to manually initialize the geometry for our layer.
   5655         [viewLayer setBounds:NSRectToCGRect([_private->layerHostingView bounds])];
   5656         [viewLayer setAnchorPoint:CGPointMake(0, [self isFlipped] ? 1 : 0)];
   5657         CGPoint layerPosition = NSPointToCGPoint([self convertPointToBase:[_private->layerHostingView frame].origin]);
   5658         [viewLayer setPosition:layerPosition];
   5659     }
   5660 
   5661     [_private->layerHostingView setLayer:viewLayer];
   5662     [_private->layerHostingView setWantsLayer:YES];
   5663 
   5664     // Parent our root layer in the container layer
   5665     [viewLayer addSublayer:layer];
   5666 
   5667     if ([[self _webView] _postsAcceleratedCompositingNotifications])
   5668         [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:[self _webView] userInfo:nil];
   5669 
   5670 #if defined(BUILDING_ON_LEOPARD)
   5671     [viewLayer setSublayerTransform:CATransform3DMakeScale(1, -1, 1)]; // setGeometryFlipped: doesn't exist on Leopard.
   5672     [self _updateLayerHostingViewPosition];
   5673 #else
   5674     // Do geometry flipping here, which flips all the compositing layers so they are top-down.
   5675     [viewLayer setGeometryFlipped:YES];
   5676 #endif
   5677 }
   5678 
   5679 - (void)detachRootLayer
   5680 {
   5681     if (_private->layerHostingView) {
   5682         [_private->layerHostingView setLayer:nil];
   5683         [_private->layerHostingView setWantsLayer:NO];
   5684         [_private->layerHostingView removeFromSuperview];
   5685         _private->layerHostingView = nil;
   5686     }
   5687 }
   5688 
   5689 #if defined(BUILDING_ON_LEOPARD)
   5690 // This method is necessary on Leopard to work around <rdar://problem/7067892>.
   5691 - (void)_updateLayerHostingViewPosition
   5692 {
   5693     if (!_private->layerHostingView)
   5694         return;
   5695 
   5696     const CGFloat maxHeight = 2048;
   5697     NSRect layerViewFrame = [self bounds];
   5698 
   5699     if (layerViewFrame.size.height > maxHeight) {
   5700         // Clamp the size of the view to <= maxHeight to avoid the bug.
   5701         layerViewFrame.size.height = maxHeight;
   5702         NSRect visibleRect = [[self enclosingScrollView] documentVisibleRect];
   5703 
   5704         // Place the top of the layer-hosting view at the top of the visibleRect.
   5705         CGFloat topOffset = NSMinY(visibleRect);
   5706         layerViewFrame.origin.y = topOffset;
   5707 
   5708         // Compensate for the moved view by adjusting the sublayer transform on the view's layer (using flipped coords).
   5709         CATransform3D flipTransform = CATransform3DMakeTranslation(0, topOffset, 0);
   5710         flipTransform = CATransform3DScale(flipTransform, 1, -1, 1);
   5711         [[_private->layerHostingView layer] setSublayerTransform:flipTransform];
   5712     }
   5713 
   5714     [_private->layerHostingView _updateLayerGeometryFromView];  // Workaround for <rdar://problem/7071636>
   5715     [_private->layerHostingView setFrame:layerViewFrame];
   5716 }
   5717 #endif // defined(BUILDING_ON_LEOPARD)
   5718 
   5719 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
   5720 {
   5721     if (_private) {
   5722         ASSERT(!_private->drawingIntoLayer);
   5723         _private->drawingIntoLayer = YES;
   5724     }
   5725 
   5726     [super drawLayer:layer inContext:ctx];
   5727 
   5728     if (_private)
   5729         _private->drawingIntoLayer = NO;
   5730 }
   5731 
   5732 - (BOOL)_web_isDrawingIntoLayer
   5733 {
   5734     return _private->drawingIntoLayer;
   5735 }
   5736 
   5737 #endif // USE(ACCELERATED_COMPOSITING)
   5738 
   5739 @end
   5740 
   5741 @implementation WebHTMLView (WebNSTextInputSupport)
   5742 
   5743 - (NSArray *)validAttributesForMarkedText
   5744 {
   5745     static NSArray *validAttributes;
   5746     if (!validAttributes) {
   5747         validAttributes = [[NSArray alloc] initWithObjects:
   5748             NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName,
   5749             NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
   5750         // NSText also supports the following attributes, but it's
   5751         // hard to tell which are really required for text input to
   5752         // work well; I have not seen any input method make use of them yet.
   5753         //     NSFontAttributeName, NSForegroundColorAttributeName,
   5754         //     NSBackgroundColorAttributeName, NSLanguageAttributeName.
   5755         CFRetain(validAttributes);
   5756     }
   5757     LOG(TextInput, "validAttributesForMarkedText -> (...)");
   5758     return validAttributes;
   5759 }
   5760 
   5761 - (NSTextInputContext *)inputContext
   5762 {
   5763     return _private->exposeInputContext ? [super inputContext] : nil;
   5764 }
   5765 
   5766 - (NSAttributedString *)textStorage
   5767 {
   5768     if (!_private->exposeInputContext) {
   5769         LOG(TextInput, "textStorage -> nil");
   5770         return nil;
   5771     }
   5772     NSAttributedString *result = [self attributedSubstringFromRange:NSMakeRange(0, UINT_MAX)];
   5773 
   5774     LOG(TextInput, "textStorage -> \"%@\"", result ? [result string] : @"");
   5775 
   5776     // We have to return an empty string rather than null to prevent TSM from calling -string
   5777     return result ? result : [[[NSAttributedString alloc] initWithString:@""] autorelease];
   5778 }
   5779 
   5780 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
   5781 {
   5782     [self _executeSavedKeypressCommands];
   5783 
   5784     NSWindow *window = [self window];
   5785     WebFrame *frame = [self _frame];
   5786 
   5787     if (window)
   5788         thePoint = [window convertScreenToBase:thePoint];
   5789     thePoint = [self convertPoint:thePoint fromView:nil];
   5790 
   5791     DOMRange *range = [frame _characterRangeAtPoint:thePoint];
   5792     if (!range) {
   5793         LOG(TextInput, "characterIndexForPoint:(%f, %f) -> NSNotFound", thePoint.x, thePoint.y);
   5794         return NSNotFound;
   5795     }
   5796 
   5797     unsigned result = [frame _convertDOMRangeToNSRange:range].location;
   5798     LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
   5799     return result;
   5800 }
   5801 
   5802 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
   5803 {
   5804     [self _executeSavedKeypressCommands];
   5805 
   5806     WebFrame *frame = [self _frame];
   5807 
   5808     // Just to match NSTextView's behavior. Regression tests cannot detect this;
   5809     // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
   5810     // (type something; try ranges (1, -1) and (2, -1).
   5811     if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
   5812         theRange.length = 0;
   5813 
   5814     DOMRange *range = [frame _convertNSRangeToDOMRange:theRange];
   5815     if (!range) {
   5816         LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (0, 0, 0, 0)", theRange.location, theRange.length);
   5817         return NSMakeRect(0, 0, 0, 0);
   5818     }
   5819 
   5820     ASSERT([range startContainer]);
   5821     ASSERT([range endContainer]);
   5822 
   5823     NSRect resultRect = [frame _firstRectForDOMRange:range];
   5824     resultRect = [self convertRect:resultRect toView:nil];
   5825 
   5826     NSWindow *window = [self window];
   5827     if (window)
   5828         resultRect.origin = [window convertBaseToScreen:resultRect.origin];
   5829 
   5830     LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
   5831     return resultRect;
   5832 }
   5833 
   5834 - (NSRange)selectedRange
   5835 {
   5836     [self _executeSavedKeypressCommands];
   5837 
   5838     if (!isTextInput(core([self _frame]))) {
   5839         LOG(TextInput, "selectedRange -> (NSNotFound, 0)");
   5840         return NSMakeRange(NSNotFound, 0);
   5841     }
   5842     NSRange result = [[self _frame] _selectedNSRange];
   5843 
   5844     LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length);
   5845     return result;
   5846 }
   5847 
   5848 - (NSRange)markedRange
   5849 {
   5850     [self _executeSavedKeypressCommands];
   5851 
   5852     WebFrame *webFrame = [self _frame];
   5853     Frame* coreFrame = core(webFrame);
   5854     if (!coreFrame)
   5855         return NSMakeRange(0, 0);
   5856     NSRange result = [webFrame _convertToNSRange:coreFrame->editor()->compositionRange().get()];
   5857 
   5858     LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
   5859     return result;
   5860 }
   5861 
   5862 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange
   5863 {
   5864     [self _executeSavedKeypressCommands];
   5865 
   5866     WebFrame *frame = [self _frame];
   5867     Frame* coreFrame = core(frame);
   5868     if (!isTextInput(coreFrame) || isInPasswordField(coreFrame)) {
   5869         LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
   5870         return nil;
   5871     }
   5872     RefPtr<Range> range = [frame _convertToDOMRange:nsRange];
   5873     if (!range) {
   5874         LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
   5875         return nil;
   5876     }
   5877 
   5878     NSAttributedString *result = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
   5879 
   5880     // [WebHTMLConverter editingAttributedStringFromRange:]  insists on inserting a trailing
   5881     // whitespace at the end of the string which breaks the ATOK input method.  <rdar://problem/5400551>
   5882     // To work around this we truncate the resultant string to the correct length.
   5883     if ([result length] > nsRange.length) {
   5884         ASSERT([result length] == nsRange.length + 1);
   5885         ASSERT([[result string] characterAtIndex:nsRange.length] == '\n' || [[result string] characterAtIndex:nsRange.length] == ' ');
   5886         result = [result attributedSubstringFromRange:NSMakeRange(0, nsRange.length)];
   5887     }
   5888     LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result string]);
   5889     return result;
   5890 }
   5891 
   5892 // test for 10.4 because of <rdar://problem/4243463>
   5893 #ifdef BUILDING_ON_TIGER
   5894 - (long)conversationIdentifier
   5895 {
   5896     return (long)self;
   5897 }
   5898 #else
   5899 - (NSInteger)conversationIdentifier
   5900 {
   5901     return (NSInteger)self;
   5902 }
   5903 #endif
   5904 
   5905 - (BOOL)hasMarkedText
   5906 {
   5907     Frame* coreFrame = core([self _frame]);
   5908     BOOL result = coreFrame && coreFrame->editor()->hasComposition();
   5909 
   5910     if (result) {
   5911         // A saved command can confirm a composition, but it cannot start a new one.
   5912         [self _executeSavedKeypressCommands];
   5913         result = coreFrame->editor()->hasComposition();
   5914     }
   5915 
   5916     LOG(TextInput, "hasMarkedText -> %u", result);
   5917     return result;
   5918 }
   5919 
   5920 - (void)unmarkText
   5921 {
   5922     [self _executeSavedKeypressCommands];
   5923 
   5924     LOG(TextInput, "unmarkText");
   5925 
   5926     // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
   5927     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
   5928 
   5929     if (parameters) {
   5930         parameters->eventInterpretationHadSideEffects = true;
   5931         parameters->consumedByIM = false;
   5932     }
   5933 
   5934     if (Frame* coreFrame = core([self _frame]))
   5935         coreFrame->editor()->confirmComposition();
   5936 }
   5937 
   5938 static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
   5939 {
   5940     int length = [[string string] length];
   5941 
   5942     int i = 0;
   5943     while (i < length) {
   5944         NSRange range;
   5945         NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
   5946 
   5947         if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
   5948             Color color = Color::black;
   5949             if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
   5950                 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
   5951             result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
   5952         }
   5953 
   5954         i = range.location + range.length;
   5955     }
   5956 }
   5957 
   5958 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
   5959 {
   5960     [self _executeSavedKeypressCommands];
   5961 
   5962     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
   5963     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
   5964 
   5965     LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length);
   5966 
   5967     // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
   5968     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
   5969 
   5970     if (parameters) {
   5971         parameters->eventInterpretationHadSideEffects = true;
   5972         parameters->consumedByIM = false;
   5973     }
   5974 
   5975     Frame* coreFrame = core([self _frame]);
   5976     if (!coreFrame)
   5977         return;
   5978 
   5979     if (![self _isEditable])
   5980         return;
   5981 
   5982     Vector<CompositionUnderline> underlines;
   5983     NSString *text;
   5984     NSRange replacementRange = { NSNotFound, 0 };
   5985 
   5986     if (isAttributedString) {
   5987         // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
   5988         text = [string string];
   5989         NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])];
   5990         LOG(TextInput, "    ReplacementRange: %@", rangeString);
   5991         // The AppKit adds a 'secret' property to the string that contains the replacement range.
   5992         // The replacement range is the range of the the text that should be replaced with the new string.
   5993         if (rangeString)
   5994             replacementRange = NSRangeFromString(rangeString);
   5995 
   5996         extractUnderlines(string, underlines);
   5997     } else
   5998         text = string;
   5999 
   6000     if (replacementRange.location != NSNotFound)
   6001         [[self _frame] _selectNSRange:replacementRange];
   6002 
   6003     coreFrame->editor()->setComposition(text, underlines, newSelRange.location, NSMaxRange(newSelRange));
   6004 }
   6005 
   6006 - (void)doCommandBySelector:(SEL)selector
   6007 {
   6008     LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
   6009 
   6010     // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
   6011     // The same call to interpretKeyEvents can do more than one command.
   6012     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
   6013     if (parameters)
   6014         parameters->consumedByIM = false;
   6015 
   6016     KeyboardEvent* event = parameters ? parameters->event : 0;
   6017     bool shouldSaveCommand = parameters && parameters->shouldSaveCommands;
   6018 
   6019     // As in insertText:, we assume that the call comes from an input method if there is marked text.
   6020     RefPtr<Frame> coreFrame = core([self _frame]);
   6021     bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition();
   6022 
   6023     if (event && shouldSaveCommand && !isFromInputMethod)
   6024         event->keypressCommands().append(KeypressCommand(NSStringFromSelector(selector)));
   6025     else {
   6026         // Make sure that only direct calls to doCommandBySelector: see the parameters by setting to 0.
   6027         _private->interpretKeyEventsParameters = 0;
   6028 
   6029         bool eventWasHandled;
   6030 
   6031         WebView *webView = [self _webView];
   6032         if ([[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector])
   6033             eventWasHandled = true;
   6034         else {
   6035             Editor::Command command = [self coreCommandBySelector:selector];
   6036             if (command.isSupported())
   6037                 eventWasHandled = command.execute(event);
   6038             else {
   6039                 // If WebKit does not support this command, we need to pass the selector to super.
   6040                 _private->selectorForDoCommandBySelector = selector;
   6041 
   6042                 // The sink does two things: 1) Tells us if the responder went unhandled, and
   6043                 // 2) prevents any NSBeep; we don't ever want to beep here.
   6044                 WebResponderChainSink *sink = [[WebResponderChainSink alloc] initWithResponderChain:self];
   6045                 [super doCommandBySelector:selector];
   6046                 eventWasHandled = ![sink receivedUnhandledCommand];
   6047                 [sink detach];
   6048                 [sink release];
   6049 
   6050                 _private->selectorForDoCommandBySelector = 0;
   6051             }
   6052         }
   6053 
   6054         if (parameters)
   6055             parameters->eventInterpretationHadSideEffects |= eventWasHandled;
   6056 
   6057         _private->interpretKeyEventsParameters = parameters;
   6058     }
   6059 }
   6060 
   6061 - (void)insertText:(id)string
   6062 {
   6063     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
   6064     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
   6065 
   6066     LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
   6067 
   6068     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
   6069     if (parameters)
   6070         parameters->consumedByIM = false;
   6071 
   6072     RefPtr<Frame> coreFrame = core([self _frame]);
   6073     NSString *text;
   6074     NSRange replacementRange = { NSNotFound, 0 };
   6075     bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition();
   6076 
   6077     if (isAttributedString) {
   6078         // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
   6079         // It does not look like any input methods ever use insertText: with attributes other than NSTextInputReplacementRangeAttributeName.
   6080         text = [string string];
   6081         NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])];
   6082         LOG(TextInput, "    ReplacementRange: %@", rangeString);
   6083         if (rangeString) {
   6084             replacementRange = NSRangeFromString(rangeString);
   6085             isFromInputMethod = true;
   6086         }
   6087     } else
   6088         text = string;
   6089 
   6090     KeyboardEvent* event = parameters ? parameters->event : 0;
   6091 
   6092     // insertText can be called for several reasons:
   6093     // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later.
   6094     // - If it's from an input method, then we should go ahead and insert the text now. We assume it's from the input method if we have marked text.
   6095     // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method.
   6096     // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
   6097     // then we also execute it immediately, as there will be no other chance.
   6098     bool shouldSaveCommand = parameters && parameters->shouldSaveCommands;
   6099     if (event && shouldSaveCommand && !isFromInputMethod) {
   6100         event->keypressCommands().append(KeypressCommand("insertText:", text));
   6101         return;
   6102     }
   6103 
   6104     if (!coreFrame || !coreFrame->editor()->canEdit())
   6105         return;
   6106 
   6107     if (replacementRange.location != NSNotFound)
   6108         [[self _frame] _selectNSRange:replacementRange];
   6109 
   6110     bool eventHandled = false;
   6111     String eventText = text;
   6112     eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
   6113     if (!coreFrame->editor()->hasComposition()) {
   6114         // An insertText: might be handled by other responders in the chain if we don't handle it.
   6115         // One example is space bar that results in scrolling down the page.
   6116         eventHandled = coreFrame->editor()->insertText(eventText, event);
   6117     } else {
   6118         eventHandled = true;
   6119         coreFrame->editor()->confirmComposition(eventText);
   6120     }
   6121 
   6122     if (parameters)
   6123         parameters->eventInterpretationHadSideEffects |= eventHandled;
   6124 }
   6125 
   6126 - (void)_updateSecureInputState
   6127 {
   6128     if (![[self window] isKeyWindow] || ([[self window] firstResponder] != self && !_private->_forceUpdateSecureInputState)) {
   6129         if (_private->isInSecureInputState) {
   6130             DisableSecureEventInput();
   6131             _private->isInSecureInputState = NO;
   6132         }
   6133         return;
   6134     }
   6135 
   6136     Frame* coreFrame = core([self _frame]);
   6137     if (!coreFrame)
   6138         return;
   6139 
   6140     if (isInPasswordField(coreFrame)) {
   6141         if (!_private->isInSecureInputState)
   6142             EnableSecureEventInput();
   6143         _private->isInSecureInputState = YES;
   6144         // WebKit substitutes nil for input context when in password field, which corresponds to null TSMDocument. So, there is
   6145         // no need to call TSMGetActiveDocument(), which may return an incorrect result when selection hasn't been yet updated
   6146         // after focusing a node.
   6147         static CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
   6148         TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag, sizeof(CFArrayRef), &inputSources);
   6149     } else {
   6150         if (_private->isInSecureInputState)
   6151             DisableSecureEventInput();
   6152         _private->isInSecureInputState = NO;
   6153         TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
   6154     }
   6155 }
   6156 
   6157 - (void)_updateSelectionForInputManager
   6158 {
   6159     Frame* coreFrame = core([self _frame]);
   6160     if (!coreFrame)
   6161         return;
   6162 
   6163     BOOL exposeInputContext = isTextInput(coreFrame) && !isInPasswordField(coreFrame);
   6164     if (exposeInputContext != _private->exposeInputContext) {
   6165         _private->exposeInputContext = exposeInputContext;
   6166         // Let AppKit cache a potentially changed input context.
   6167         // WebCore routinely sets the selection to None when editing, and IMs become unhappy when an input context suddenly turns nil, see bug 26009.
   6168         if (!coreFrame->selection()->isNone())
   6169             [NSApp updateWindows];
   6170     }
   6171 
   6172     [self _updateSecureInputState];
   6173 
   6174     if (!coreFrame->editor()->hasComposition())
   6175         return;
   6176 
   6177     if (coreFrame->editor()->ignoreCompositionSelectionChange())
   6178         return;
   6179 
   6180     unsigned start;
   6181     unsigned end;
   6182     if (coreFrame->editor()->getCompositionSelection(start, end))
   6183         [[NSInputManager currentInputManager] markedTextSelectionChanged:NSMakeRange(start, end - start) client:self];
   6184     else {
   6185         coreFrame->editor()->confirmCompositionWithoutDisturbingSelection();
   6186         [[NSInputManager currentInputManager] markedTextAbandoned:self];
   6187     }
   6188 }
   6189 
   6190 @end
   6191 
   6192 @implementation WebHTMLView (WebDocumentPrivateProtocols)
   6193 
   6194 - (NSRect)selectionRect
   6195 {
   6196     if (![self _hasSelection])
   6197         return NSZeroRect;
   6198     return core([self _frame])->selection()->bounds();
   6199 }
   6200 
   6201 - (NSArray *)selectionTextRects
   6202 {
   6203     if (![self _hasSelection])
   6204         return nil;
   6205 
   6206     Vector<FloatRect> list;
   6207     if (Frame* coreFrame = core([self _frame]))
   6208         coreFrame->selection()->getClippedVisibleTextRectangles(list);
   6209 
   6210     size_t size = list.size();
   6211 
   6212     NSMutableArray *result = [NSMutableArray arrayWithCapacity:size];
   6213 
   6214     for (size_t i = 0; i < size; ++i)
   6215         [result addObject:[NSValue valueWithRect:list[i]]];
   6216 
   6217     return result;
   6218 }
   6219 
   6220 - (NSView *)selectionView
   6221 {
   6222     return self;
   6223 }
   6224 
   6225 - (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText
   6226 {
   6227     if (![self _hasSelection])
   6228         return nil;
   6229     return core([self _frame])->selectionImage(forceBlackText);
   6230 }
   6231 
   6232 - (NSRect)selectionImageRect
   6233 {
   6234     if (![self _hasSelection])
   6235         return NSZeroRect;
   6236     return core([self _frame])->selection()->bounds();
   6237 }
   6238 
   6239 - (NSArray *)pasteboardTypesForSelection
   6240 {
   6241     if ([self _canSmartCopyOrDelete]) {
   6242         NSMutableArray *types = [[[[self class] _selectionPasteboardTypes] mutableCopy] autorelease];
   6243         [types addObject:WebSmartPastePboardType];
   6244         return types;
   6245     } else {
   6246         return [[self class] _selectionPasteboardTypes];
   6247     }
   6248 }
   6249 
   6250 - (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
   6251 {
   6252     [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:nil];
   6253 }
   6254 
   6255 - (void)selectAll
   6256 {
   6257     Frame* coreFrame = core([self _frame]);
   6258     if (coreFrame)
   6259         coreFrame->selection()->selectAll();
   6260 }
   6261 
   6262 - (void)deselectAll
   6263 {
   6264     Frame* coreFrame = core([self _frame]);
   6265     if (!coreFrame)
   6266         return;
   6267     coreFrame->selection()->clear();
   6268 }
   6269 
   6270 - (NSString *)string
   6271 {
   6272     return [[self _frame] _stringForRange:[self _documentRange]];
   6273 }
   6274 
   6275 - (NSAttributedString *)_attributeStringFromDOMRange:(DOMRange *)range
   6276 {
   6277     NSAttributedString *attributedString;
   6278 #if !LOG_DISABLED
   6279     double start = CFAbsoluteTimeGetCurrent();
   6280 #endif
   6281     attributedString = [[[NSAttributedString alloc] _initWithDOMRange:range] autorelease];
   6282 #if !LOG_DISABLED
   6283     double duration = CFAbsoluteTimeGetCurrent() - start;
   6284     LOG(Timing, "creating attributed string from selection took %f seconds.", duration);
   6285 #endif
   6286     return attributedString;
   6287 }
   6288 
   6289 - (NSAttributedString *)attributedString
   6290 {
   6291     DOMDocument *document = [[self _frame] DOMDocument];
   6292     NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[document _documentRange]];
   6293     if (!attributedString) {
   6294         Document* coreDocument = core(document);
   6295         attributedString = [WebHTMLConverter editingAttributedStringFromRange:Range::create(coreDocument, coreDocument, 0, coreDocument, coreDocument->childNodeCount()).get()];
   6296     }
   6297     return attributedString;
   6298 }
   6299 
   6300 - (NSString *)selectedString
   6301 {
   6302     return [[self _frame] _selectedString];
   6303 }
   6304 
   6305 - (NSAttributedString *)selectedAttributedString
   6306 {
   6307     NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[self _selectedRange]];
   6308     if (!attributedString) {
   6309         Frame* coreFrame = core([self _frame]);
   6310         if (coreFrame) {
   6311             RefPtr<Range> range = coreFrame->selection()->selection().toNormalizedRange();
   6312             attributedString = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
   6313         }
   6314     }
   6315     return attributedString;
   6316 }
   6317 
   6318 - (BOOL)supportsTextEncoding
   6319 {
   6320     return YES;
   6321 }
   6322 
   6323 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
   6324 {
   6325     return [self _findString:string options:(forward ? 0 : WebFindOptionsBackwards) | (caseFlag ? 0 : WebFindOptionsCaseInsensitive) | (wrapFlag ? WebFindOptionsWrapAround : 0) | (startInSelection ? WebFindOptionsStartInSelection : 0)];
   6326 }
   6327 
   6328 @end
   6329 
   6330 @implementation WebHTMLView (WebDocumentInternalProtocols)
   6331 
   6332 - (NSDictionary *)elementAtPoint:(NSPoint)point
   6333 {
   6334     return [self elementAtPoint:point allowShadowContent:NO];
   6335 }
   6336 
   6337 - (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow
   6338 {
   6339     Frame* coreFrame = core([self _frame]);
   6340     if (!coreFrame)
   6341         return nil;
   6342     return [[[WebElementDictionary alloc] initWithHitTestResult:coreFrame->eventHandler()->hitTestResultAtPoint(IntPoint(point), allow)] autorelease];
   6343 }
   6344 
   6345 - (NSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options limit:(NSUInteger)limit markMatches:(BOOL)markMatches
   6346 {
   6347     Frame* coreFrame = core([self _frame]);
   6348     if (!coreFrame)
   6349         return 0;
   6350 
   6351     return coreFrame->editor()->countMatchesForText(string, core(range), coreOptions(options), limit, markMatches);
   6352 }
   6353 
   6354 - (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
   6355 {
   6356     Frame* coreFrame = core([self _frame]);
   6357     if (!coreFrame)
   6358         return;
   6359     coreFrame->editor()->setMarkedTextMatchesAreHighlighted(newValue);
   6360 }
   6361 
   6362 - (BOOL)markedTextMatchesAreHighlighted
   6363 {
   6364     Frame* coreFrame = core([self _frame]);
   6365     return coreFrame && coreFrame->editor()->markedTextMatchesAreHighlighted();
   6366 }
   6367 
   6368 - (void)unmarkAllTextMatches
   6369 {
   6370     Frame* coreFrame = core([self _frame]);
   6371     if (!coreFrame)
   6372         return;
   6373     Document* document = coreFrame->document();
   6374     if (!document)
   6375         return;
   6376     document->markers()->removeMarkers(DocumentMarker::TextMatch);
   6377 }
   6378 
   6379 - (NSArray *)rectsForTextMatches
   6380 {
   6381     Frame* coreFrame = core([self _frame]);
   6382     if (!coreFrame)
   6383         return [NSArray array];
   6384     Document* document = coreFrame->document();
   6385     if (!document)
   6386         return [NSArray array];
   6387 
   6388     Vector<IntRect> rects = document->markers()->renderedRectsForMarkers(DocumentMarker::TextMatch);
   6389     unsigned count = rects.size();
   6390     NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
   6391     for (unsigned index = 0; index < count; ++index)
   6392         [result addObject:[NSValue valueWithRect:rects[index]]];
   6393     return result;
   6394 }
   6395 
   6396 - (BOOL)_findString:(NSString *)string options:(WebFindOptions)options
   6397 {
   6398     if (![string length])
   6399         return NO;
   6400     Frame* coreFrame = core([self _frame]);
   6401     return coreFrame && coreFrame->editor()->findString(string, coreOptions(options));
   6402 }
   6403 
   6404 @end
   6405 
   6406 // This is used by AppKit and is included here so that WebDataProtocolScheme is only defined once.
   6407 @implementation NSURL (WebDataURL)
   6408 
   6409 + (NSURL *)_web_uniqueWebDataURL
   6410 {
   6411     CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
   6412     NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
   6413     CFRelease(UUIDRef);
   6414     NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", WebDataProtocolScheme, UUIDString]];
   6415     CFRelease(UUIDString);
   6416     return URL;
   6417 }
   6418 
   6419 @end
   6420 
   6421 @implementation WebResponderChainSink
   6422 
   6423 - (id)initWithResponderChain:(NSResponder *)chain
   6424 {
   6425     self = [super init];
   6426     _lastResponderInChain = chain;
   6427     while (NSResponder *next = [_lastResponderInChain nextResponder])
   6428         _lastResponderInChain = next;
   6429     [_lastResponderInChain setNextResponder:self];
   6430     return self;
   6431 }
   6432 
   6433 - (void)detach
   6434 {
   6435     [_lastResponderInChain setNextResponder:nil];
   6436     _lastResponderInChain = nil;
   6437 }
   6438 
   6439 - (BOOL)receivedUnhandledCommand
   6440 {
   6441     return _receivedUnhandledCommand;
   6442 }
   6443 
   6444 - (void)noResponderFor:(SEL)selector
   6445 {
   6446     _receivedUnhandledCommand = YES;
   6447 }
   6448 
   6449 - (void)doCommandBySelector:(SEL)selector
   6450 {
   6451     _receivedUnhandledCommand = YES;
   6452 }
   6453 
   6454 - (BOOL)tryToPerform:(SEL)action with:(id)object
   6455 {
   6456     _receivedUnhandledCommand = YES;
   6457     return YES;
   6458 }
   6459 
   6460 @end
   6461