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