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