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