Reviewed by Darin.
[WebKit-https.git] / WebKit / WebView / WebHTMLView.m
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import <WebKit/WebHTMLView.h>
30
31 #import <ApplicationServices/ApplicationServices.h>
32 #import <WebKit/DOM.h>
33 #import <WebKit/DOMExtensions.h>
34 #import <WebKit/DOMPrivate.h>
35 #import <WebKit/WebArchive.h>
36 #import <WebKit/WebArchiver.h>
37 #import <WebKit/WebBaseNetscapePluginViewInternal.h>
38 #import <WebKit/WebFrameBridge.h>
39 #import <WebKit/WebClipView.h>
40 #import <WebKit/WebDataProtocol.h>
41 #import <WebKit/WebDataSourceInternal.h>
42 #import <WebKit/WebDefaultUIDelegate.h>
43 #import <WebKit/WebDocumentInternal.h>
44 #import <WebKit/WebDOMOperationsPrivate.h>
45 #import <WebKit/WebEditingDelegate.h>
46 #import <WebKit/WebElementDictionary.h>
47 #import <WebKit/WebFramePrivate.h>
48 #import <WebKit/WebFrameInternal.h>
49 #import <WebKit/WebFrameViewInternal.h>
50 #import <WebKit/WebHTMLViewInternal.h>
51 #import <WebKit/WebHTMLRepresentationPrivate.h>
52 #import <WebKit/WebKitLogging.h>
53 #import <WebKit/WebKitNSStringExtras.h>
54 #import <WebKit/WebNetscapePluginEmbeddedView.h>
55 #import <WebKit/WebNSAttributedStringExtras.h>
56 #import <WebKit/WebNSEventExtras.h>
57 #import <WebKit/WebNSFileManagerExtras.h>
58 #import <WebKit/WebNSImageExtras.h>
59 #import <WebKit/WebNSObjectExtras.h>
60 #import <WebKit/WebNSPasteboardExtras.h>
61 #import <WebKit/WebNSPrintOperationExtras.h>
62 #import <WebKit/WebNSURLExtras.h>
63 #import <WebKit/WebNSViewExtras.h>
64 #import <WebKit/WebPluginController.h>
65 #import <WebKit/WebPreferences.h>
66 #import <WebKit/WebPreferencesPrivate.h>
67 #import <WebKit/WebResourcePrivate.h>
68 #import <WebKit/WebStringTruncator.h>
69 #import <WebKit/WebUIDelegatePrivate.h>
70 #import <WebKit/WebViewInternal.h>
71 #import <WebKitSystemInterface.h>
72 #import <WebCore/WebCoreTextRenderer.h>
73
74 #import <AppKit/NSAccessibility.h>
75
76 // Included so usage of _NSSoftLinkingGetFrameworkFuncPtr will compile
77 #import <mach-o/dyld.h> 
78
79
80 // need to declare this because AppKit does not make it available as API or SPI
81 extern NSString *NSMarkedClauseSegmentAttributeName; 
82 extern NSString *NSTextInputReplacementRangeAttributeName; 
83
84 // Kill ring calls. Would be better to use NSKillRing.h, but that's not available in SPI.
85 void _NSInitializeKillRing(void);
86 void _NSAppendToKillRing(NSString *);
87 void _NSPrependToKillRing(NSString *);
88 NSString *_NSYankFromKillRing(void);
89 NSString *_NSYankPreviousFromKillRing(void);
90 void _NSNewKillRingSequence(void);
91 void _NSSetKillRingToYankedState(void);
92 void _NSResetKillRingOperationFlag(void);
93
94 @interface NSView (AppKitSecretsIKnowAbout)
95 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
96 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
97 - (NSRect)_dirtyRect;
98 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
99 - (void)_propagateDirtyRectsToOpaqueAncestors;
100 @end
101
102 @interface NSApplication (AppKitSecretsIKnowAbout)
103 - (void)speakString:(NSString *)string;
104 @end
105
106 @interface NSWindow (AppKitSecretsIKnowAbout)
107 - (id)_newFirstResponderAfterResigning;
108 @end
109
110 @interface NSAttributedString (AppKitSecretsIKnowAbout)
111 - (id)_initWithDOMRange:(DOMRange *)domRange;
112 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
113 @end
114
115 @interface NSSpellChecker (CurrentlyPrivateForTextView)
116 - (void)learnWord:(NSString *)word;
117 @end
118
119 // By imaging to a width a little wider than the available pixels,
120 // thin pages will be scaled down a little, matching the way they
121 // print in IE and Camino. This lets them use fewer sheets than they
122 // would otherwise, which is presumably why other browsers do this.
123 // Wide pages will be scaled down more than this.
124 #define PrintingMinimumShrinkFactor     1.25
125
126 // This number determines how small we are willing to reduce the page content
127 // in order to accommodate the widest line. If the page would have to be
128 // reduced smaller to make the widest line fit, we just clip instead (this
129 // behavior matches MacIE and Mozilla, at least)
130 #define PrintingMaximumShrinkFactor     2.0
131
132 #define AUTOSCROLL_INTERVAL             0.1
133
134 #define DRAG_LABEL_BORDER_X             4.0
135 #define DRAG_LABEL_BORDER_Y             2.0
136 #define DRAG_LABEL_RADIUS               5.0
137 #define DRAG_LABEL_BORDER_Y_OFFSET              2.0
138
139 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP        120.0
140 #define MAX_DRAG_LABEL_WIDTH                    320.0
141
142 #define DRAG_LINK_LABEL_FONT_SIZE   11.0
143 #define DRAG_LINK_URL_FONT_SIZE   10.0
144
145 // Any non-zero value will do, but using something recognizable might help us debug some day.
146 #define TRACKING_RECT_TAG 0xBADFACE
147
148 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
149 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
150
151 #define STANDARD_WEIGHT 5
152 #define MIN_BOLD_WEIGHT 9
153 #define STANDARD_BOLD_WEIGHT 10
154
155 typedef enum {
156     deleteSelectionAction,
157     deleteKeyAction,
158     forwardDeleteKeyAction
159 } WebDeletionAction;
160
161 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
162 static BOOL forceNSViewHitTest = NO;
163
164 // if YES, do the "top WebHTMLView" it test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721])
165 static BOOL forceWebHTMLViewHitTest = NO;
166
167 // Used to avoid linking with ApplicationServices framework for _DCMDictionaryServiceWindowShow
168 void *_NSSoftLinkingGetFrameworkFuncPtr(NSString *inUmbrellaFrameworkName,
169                                         NSString *inFrameworkName,
170                                         const char *inFuncName,
171                                         const struct mach_header **ioCachedFrameworkImageHeaderPtr);
172
173
174 @interface WebHTMLView (WebTextSizing) <_WebDocumentTextSizing>
175 @end
176
177 @interface WebHTMLView (WebHTMLViewFileInternal)
178 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
179 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText chosePlainText:(BOOL *)chosePlainText;
180 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
181 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
182 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
183 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
184 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
185 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
186 - (float)_calculatePrintHeight;
187 - (void)_updateTextSizeMultiplier;
188 - (DOMRange *)_selectedRange;
189 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
190 - (void)_deleteRange:(DOMRange *)range 
191             killRing:(BOOL)killRing 
192              prepend:(BOOL)prepend 
193        smartDeleteOK:(BOOL)smartDeleteOK
194       deletionAction:(WebDeletionAction)deletionAction
195          granularity:(WebBridgeSelectionGranularity)granularity;
196 - (void)_deleteSelection;
197 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard;
198 - (NSView *)_hitViewForEvent:(NSEvent *)event;
199 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
200 - (DOMRange *)_documentRange;
201 - (WebFrameBridge *)_bridge;
202 @end
203
204 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
205 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize;
206 @end
207
208 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
209 - (void)_updateSelectionForInputManager;
210 - (void)_insertText:(NSString *)text selectInsertedText:(BOOL)selectText;
211 @end
212
213 @interface WebHTMLView (WebEditingStyleSupport)
214 - (DOMCSSStyleDeclaration *)_emptyStyle;
215 - (NSString *)_colorAsString:(NSColor *)color;
216 @end
217
218 @interface NSView (WebHTMLViewFileInternal)
219 - (void)_web_setPrintingModeRecursive;
220 - (void)_web_clearPrintingModeRecursive;
221 - (void)_web_layoutIfNeededRecursive;
222 - (void)_web_layoutIfNeededRecursive:(NSRect)rect testDirtyRect:(bool)testDirtyRect;
223 @end
224
225 @interface NSMutableDictionary (WebHTMLViewFileInternal)
226 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
227 @end
228
229 // Handles the complete: text command
230 @interface WebTextCompleteController : NSObject
231 {
232 @private
233     WebHTMLView *_view;
234     NSWindow *_popupWindow;
235     NSTableView *_tableView;
236     NSArray *_completions;
237     NSString *_originalString;
238     int prefixLength;
239 }
240 - (id)initWithHTMLView:(WebHTMLView *)view;
241 - (void)doCompletion;
242 - (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft;
243 - (BOOL)filterKeyDown:(NSEvent *)event;
244 - (void)_reflectSelection;
245 @end
246
247 @implementation WebHTMLViewPrivate
248
249 - (void)dealloc
250 {
251     ASSERT(autoscrollTimer == nil);
252     ASSERT(autoscrollTriggerEvent == nil);
253     
254     [mouseDownEvent release];
255     [draggingImageURL release];
256     [pluginController release];
257     [toolTip release];
258     [compController release];
259     [firstResponderTextViewAtMouseDownTime release];
260     [dataSource release];
261     [highlighters release];
262
263     [super dealloc];
264 }
265
266 @end
267
268 @implementation WebHTMLView (WebHTMLViewFileInternal)
269
270 - (DOMRange *)_documentRange
271 {
272     return [[[self _bridge] DOMDocument] _documentRange];
273 }
274
275 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
276 {
277     NSArray *imageMIMETypes = [WebFrameBridge supportedImageResourceMIMETypes];
278     NSEnumerator *enumerator = [paths objectEnumerator];
279     NSString *path;
280     
281     while ((path = [enumerator nextObject]) != nil) {
282         NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
283         if ([imageMIMETypes containsObject:MIMEType]) {
284             return YES;
285         }
286     }
287     
288     return NO;
289 }
290
291 - (WebDataSource *)_dataSource
292 {
293     return _private->dataSource;
294 }
295
296 - (WebFrameBridge *)_bridge
297 {
298     return [_private->dataSource _bridge];
299 }
300
301 - (WebView *)_webView
302 {
303     return [_private->dataSource _webView];
304 }
305
306 - (WebFrameView *)_frameView
307 {
308     return [[_private->dataSource webFrame] frameView];
309 }
310
311 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
312 {
313     DOMDocumentFragment *fragment;
314     NSArray *imageMIMETypes = [WebFrameBridge supportedImageResourceMIMETypes];
315     NSEnumerator *enumerator = [paths objectEnumerator];
316     WebDataSource *dataSource = [self _dataSource];
317     NSMutableArray *domNodes = [[NSMutableArray alloc] init];
318     NSString *path;
319     
320     while ((path = [enumerator nextObject]) != nil) {
321         NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
322         if ([imageMIMETypes containsObject:MIMEType]) {
323             WebResource *resource = [[WebResource alloc] initWithData:[NSData dataWithContentsOfFile:path]
324                                                                   URL:[NSURL fileURLWithPath:path]
325                                                              MIMEType:MIMEType 
326                                                      textEncodingName:nil
327                                                             frameName:nil];
328             if (resource) {
329                 [domNodes addObject:[dataSource _imageElementWithImageResource:resource]];
330                 [resource release];
331             }
332         } else {
333             // Non-image file types
334             NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
335             [domNodes addObject:[[[self _bridge] DOMDocument] createTextNode: url]];
336         }
337     }
338     
339     fragment = [[self _bridge] documentFragmentWithNodesAsParagraphs:domNodes]; 
340     
341     [domNodes release];
342     
343     return [fragment firstChild] != nil ? fragment : nil;
344 }
345
346 + (NSArray *)_excludedElementsForAttributedStringConversion
347 {
348     static NSArray *elements = nil;
349     if (elements == nil) {
350         elements = [[NSArray alloc] initWithObjects:
351             // Omit style since we want style to be inline so the fragment can be easily inserted.
352             @"style",
353             // Omit xml so the result is not XHTML.
354             @"xml", 
355             // Omit tags that will get stripped when converted to a fragment anyway.
356             @"doctype", @"html", @"head", @"body",
357             // Omit deprecated tags.
358             @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
359             // Omit object so no file attachments are part of the fragment.
360             @"object", nil];
361     }
362     return elements;
363 }
364
365 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText chosePlainText:(BOOL *)chosePlainText
366 {
367     NSArray *types = [pasteboard types];
368     *chosePlainText = NO;
369
370     if ([types containsObject:WebArchivePboardType]) {
371         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
372         if (archive) {
373             DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
374             [archive release];
375             if (fragment) {
376                 return fragment;
377             }
378         }
379     }
380     
381     if ([types containsObject:NSFilenamesPboardType]) {
382         DOMDocumentFragment *fragment = [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
383         if (fragment != nil) {
384             return fragment;
385         }
386     }
387     
388     NSURL *URL;
389     
390     if ([types containsObject:NSHTMLPboardType]) {
391         NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
392         // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
393         if ([HTMLString hasPrefix:@"Version:"]) {
394             NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
395             if (range.location != NSNotFound) {
396                 HTMLString = [HTMLString substringFromIndex:range.location];
397             }
398         }
399         if ([HTMLString length] != 0) {
400             return [[self _bridge] documentFragmentWithMarkupString:HTMLString baseURLString:nil];
401         }
402     }
403         
404     NSAttributedString *string = nil;
405     if ([types containsObject:NSRTFDPboardType]) {
406         string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
407     }
408     if (string == nil && [types containsObject:NSRTFPboardType]) {
409         string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
410     }
411     if (string != nil) {
412         NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
413             [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
414             self, @"WebResourceHandler", nil];
415         NSArray *subresources;
416         DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length]) 
417                                                           document:[[self _bridge] DOMDocument] 
418                                                 documentAttributes:documentAttributes
419                                                       subresources:&subresources];
420         [documentAttributes release];
421         [string release];
422         return fragment;
423     }
424     
425     if ([types containsObject:NSTIFFPboardType]) {
426         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
427                                                               URL:[NSURL _web_uniqueWebDataURLWithRelativeString:@"/image.tiff"]
428                                                          MIMEType:@"image/tiff" 
429                                                  textEncodingName:nil
430                                                         frameName:nil];
431         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
432         [resource release];
433         return fragment;
434     }
435     
436     if ([types containsObject:NSPICTPboardType]) {
437         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
438                                                               URL:[NSURL _web_uniqueWebDataURLWithRelativeString:@"/image.pict"]
439                                                          MIMEType:@"image/pict" 
440                                                  textEncodingName:nil
441                                                         frameName:nil];
442         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
443         [resource release];
444         return fragment;
445     }    
446     
447     if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
448         DOMDocument* document = [[self _bridge] DOMDocument];
449         ASSERT(document);
450         if (document) {
451             DOMHTMLAnchorElement* anchor = (DOMHTMLAnchorElement*)[document createElement:@"a"];
452             NSString *URLString = [URL _web_userVisibleString];
453             NSString *URLTitleString = [pasteboard stringForType:WebURLNamePboardType];
454             DOMText* text = [document createTextNode:URLTitleString];
455             [anchor setHref:URLString];
456             [anchor appendChild:text];
457             DOMDocumentFragment* fragment = [document createDocumentFragment];
458             [fragment appendChild:anchor];
459             if ([URLString length] > 0)
460                 return fragment;
461         }
462     }
463     
464     if (allowPlainText && [types containsObject:NSStringPboardType]) {
465         *chosePlainText = YES;
466         return [[self _bridge] documentFragmentWithText:[pasteboard stringForType:NSStringPboardType]];
467     }
468     
469     return nil;
470 }
471
472 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
473 {
474     NSArray *types = [pasteboard types];
475     
476     if ([types containsObject:NSStringPboardType])
477         return [pasteboard stringForType:NSStringPboardType];
478     
479     NSAttributedString *attributedString = nil;
480     NSString *string;
481
482     if ([types containsObject:NSRTFDPboardType])
483         attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
484     if (attributedString == nil && [types containsObject:NSRTFPboardType])
485         attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
486     if (attributedString != nil) {
487         string = [[attributedString string] copy];
488         [attributedString release];
489         return [string autorelease];
490     }
491     
492     if ([types containsObject:NSFilenamesPboardType]) {
493         string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
494         if (string != nil)
495             return string;
496     }
497     
498     NSURL *URL;
499     
500     if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
501         string = [URL _web_userVisibleString];
502         if ([string length] > 0)
503             return string;
504     }
505     
506     return nil;
507 }
508
509 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
510 {
511     BOOL chosePlainText;
512     DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard allowPlainText:allowPlainText chosePlainText:&chosePlainText];
513     WebFrameBridge *bridge = [self _bridge];
514     if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[self _selectedRange] givenAction:WebViewInsertActionPasted]) {
515         [bridge replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:chosePlainText];
516     }
517 }
518
519 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
520 {
521     NSString *text = [self _plainTextFromPasteboard:pasteboard];
522     if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
523         [[self _bridge] replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
524 }
525
526 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
527 {
528     WebView *webView = [self _webView];
529     DOMNode *child = [fragment firstChild];
530     if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
531         return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
532     return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
533 }
534
535 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
536 {
537     WebView *webView = [self _webView];
538     return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
539 }
540
541 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
542 {
543     return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
544 }
545
546 // Calculate the vertical size of the view that fits on a single page
547 - (float)_calculatePrintHeight
548 {
549     // Obtain the print info object for the current operation
550     NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
551     
552     // Calculate the page height in points
553     NSSize paperSize = [pi paperSize];
554     return paperSize.height - [pi topMargin] - [pi bottomMargin];
555 }
556
557 - (void)_updateTextSizeMultiplier
558 {
559     [[self _bridge] setTextSizeMultiplier:[[self _webView] textSizeMultiplier]];    
560 }
561
562 - (DOMRange *)_selectedRange
563 {
564     return [[self _bridge] selectedDOMRange];
565 }
566
567 - (BOOL)_shouldDeleteRange:(DOMRange *)range
568 {
569     if (range == nil || [range collapsed])
570         return NO;
571     
572     if (![[self _bridge] canDeleteRange:range])
573         return NO;
574         
575     WebView *webView = [self _webView];
576     return [[webView _editingDelegateForwarder] webView:webView shouldDeleteDOMRange:range];
577 }
578
579 - (void)_deleteRange:(DOMRange *)range 
580             killRing:(BOOL)killRing 
581              prepend:(BOOL)prepend 
582        smartDeleteOK:(BOOL)smartDeleteOK 
583       deletionAction:(WebDeletionAction)deletionAction
584          granularity:(WebBridgeSelectionGranularity)granularity
585 {
586     WebFrameBridge *bridge = [self _bridge];
587     BOOL smartDelete = smartDeleteOK ? [self _canSmartCopyOrDelete] : NO;
588
589     BOOL startNewKillRingSequence = _private->startNewKillRingSequence;
590
591     if (killRing) {
592         if (startNewKillRingSequence) {
593             _NSNewKillRingSequence();
594         }
595         NSString *string = [bridge stringForRange:range];
596         if (prepend) {
597             _NSPrependToKillRing(string);
598         } else {
599             _NSAppendToKillRing(string);
600         }
601         startNewKillRingSequence = NO;
602     }
603
604     switch (deletionAction) {
605         case deleteSelectionAction:
606             [bridge setSelectedDOMRange:range affinity:NSSelectionAffinityDownstream closeTyping:YES];
607             [bridge deleteSelectionWithSmartDelete:smartDelete];
608             break;
609         case deleteKeyAction:
610             [bridge setSelectedDOMRange:range affinity:NSSelectionAffinityDownstream closeTyping:(granularity != WebBridgeSelectByCharacter)];
611             [bridge deleteKeyPressedWithSmartDelete:smartDelete granularity:granularity];
612             break;
613         case forwardDeleteKeyAction:
614             [bridge setSelectedDOMRange:range affinity:NSSelectionAffinityDownstream closeTyping:(granularity != WebBridgeSelectByCharacter)];
615             [bridge forwardDeleteKeyPressedWithSmartDelete:smartDelete granularity:granularity];
616             break;
617     }
618
619     _private->startNewKillRingSequence = startNewKillRingSequence;
620 }
621
622 - (void)_deleteSelection
623 {
624     [self _deleteRange:[self _selectedRange]
625               killRing:YES 
626                prepend:NO
627          smartDeleteOK:YES
628         deletionAction:deleteSelectionAction
629            granularity:WebBridgeSelectByCharacter];
630 }
631
632 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
633 {
634     return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
635 }
636
637 - (NSView *)_hitViewForEvent:(NSEvent *)event
638 {
639     // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.  
640     // Callers of this method, however, want to query the deepest view instead.
641     forceNSViewHitTest = YES;
642     NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
643     forceNSViewHitTest = NO;    
644     return hitView;
645 }
646
647 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
648 {
649     // Put HTML on the pasteboard.
650     if ([types containsObject:WebArchivePboardType]) {
651         WebArchive *archive = [WebArchiver archiveSelectionInFrame:[self _frame]];
652         [pasteboard setData:[archive data] forType:WebArchivePboardType];
653     }
654     
655     // Put the attributed string on the pasteboard (RTF/RTFD format).
656     if ([types containsObject:NSRTFDPboardType]) {
657         if (attributedString == nil) {
658             attributedString = [self selectedAttributedString];
659         }        
660         NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
661         [pasteboard setData:RTFDData forType:NSRTFDPboardType];
662     }        
663     if ([types containsObject:NSRTFPboardType]) {
664         if (attributedString == nil) {
665             attributedString = [self selectedAttributedString];
666         }
667         if ([attributedString containsAttachments]) {
668             attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
669         }
670         NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
671         [pasteboard setData:RTFData forType:NSRTFPboardType];
672     }
673     
674     // Put plain string on the pasteboard.
675     if ([types containsObject:NSStringPboardType]) {
676         // Map &nbsp; to a plain old space because this is better for source code, other browsers do it,
677         // and because HTML forces you to do this any time you want two spaces in a row.
678         NSMutableString *s = [[self selectedString] mutableCopy];
679         const unichar NonBreakingSpaceCharacter = 0xA0;
680         NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
681         [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
682         [pasteboard setString:s forType:NSStringPboardType];
683         [s release];
684     }
685     
686     if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
687         [pasteboard setData:nil forType:WebSmartPastePboardType];
688     }
689 }
690
691 @end
692
693 @implementation WebHTMLView (WebPrivate)
694
695 + (NSArray *)supportedMIMETypes
696 {
697     return [WebHTMLRepresentation supportedMIMETypes];
698 }
699
700 + (NSArray *)supportedImageMIMETypes
701 {
702     return [WebHTMLRepresentation supportedImageMIMETypes];
703 }
704
705 + (NSArray *)supportedNonImageMIMETypes
706 {
707     return [WebHTMLRepresentation supportedNonImageMIMETypes];
708 }
709
710 + (NSArray *)unsupportedTextMIMETypes
711 {
712     return [NSArray arrayWithObjects:
713         @"text/calendar",       // iCal
714         @"text/x-calendar",
715         @"text/x-vcalendar",
716         @"text/vcalendar",
717         @"text/vcard",          // vCard
718         @"text/x-vcard",
719         @"text/directory",
720         @"text/ldif",           // Netscape Address Book
721         @"text/qif",            // Quicken
722         @"text/x-qif",
723         @"text/x-csv",          // CSV (for Address Book and Microsoft Outlook)
724         @"text/x-vcf",          // vCard type used in Sun affinity app
725         @"text/rtf",            // Rich Text Format
726         nil];
727 }
728
729 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
730 {
731     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
732         location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
733         modifierFlags:[flagsChangedEvent modifierFlags]
734         timestamp:[flagsChangedEvent timestamp]
735         windowNumber:[flagsChangedEvent windowNumber]
736         context:[flagsChangedEvent context]
737         eventNumber:0 clickCount:0 pressure:0];
738
739     // Pretend it's a mouse move.
740     [[NSNotificationCenter defaultCenter]
741         postNotificationName:WKMouseMovedNotification() object:self
742         userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
743 }
744
745 - (void)_updateMouseoverWithFakeEvent
746 {
747     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
748         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
749         modifierFlags:[[NSApp currentEvent] modifierFlags]
750         timestamp:[NSDate timeIntervalSinceReferenceDate]
751         windowNumber:[[self window] windowNumber]
752         context:[[NSApp currentEvent] context]
753         eventNumber:0 clickCount:0 pressure:0];
754     
755     [self _updateMouseoverWithEvent:fakeEvent];
756 }
757
758 - (void)_frameOrBoundsChanged
759 {
760     if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
761         [self setNeedsLayout:YES];
762         [self setNeedsDisplay:YES];
763         [_private->compController endRevertingChange:NO moveLeft:NO];
764     }
765
766     NSPoint origin = [[self superview] bounds].origin;
767     if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
768         [[self _bridge] sendScrollEvent];
769         [_private->compController endRevertingChange:NO moveLeft:NO];
770         
771         WebView *webView = [self _webView];
772         [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
773     }
774     _private->lastScrollPosition = origin;
775
776     SEL selector = @selector(_updateMouseoverWithFakeEvent);
777     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:selector object:nil];
778     [self performSelector:selector withObject:nil afterDelay:0];
779 }
780
781 - (void)_setAsideSubviews
782 {
783     ASSERT(!_private->subviewsSetAside);
784     ASSERT(_private->savedSubviews == nil);
785     _private->savedSubviews = _subviews;
786     _subviews = nil;
787     _private->subviewsSetAside = YES;
788  }
789  
790  - (void)_restoreSubviews
791  {
792     ASSERT(_private->subviewsSetAside);
793     ASSERT(_subviews == nil);
794     _subviews = _private->savedSubviews;
795     _private->savedSubviews = nil;
796     _private->subviewsSetAside = NO;
797 }
798
799 // This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
800 // That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty
801 // before doing it. As a compromise, when we're opaque we do the layout only when actually asked to
802 // draw, but when we're transparent we do the layout at this stage so views behind us know that they
803 // need to be redrawn (in case the layout causes some things to get dirtied).
804 - (void)_propagateDirtyRectsToOpaqueAncestors
805 {
806     if (![[self _webView] drawsBackground]) {
807         [self _web_layoutIfNeededRecursive];
808     }
809     [super _propagateDirtyRectsToOpaqueAncestors];
810 }
811
812 // Don't let AppKit even draw subviews. We take care of that.
813 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
814 {
815     // This helps when we print as part of a larger print process.
816     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
817     BOOL wasInPrintingMode = _private->printing;
818     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
819     if (wasInPrintingMode != isPrinting) {
820         if (isPrinting) {
821             [self _web_setPrintingModeRecursive];
822         } else {
823             [self _web_clearPrintingModeRecursive];
824         }
825     }
826
827     [self _web_layoutIfNeededRecursive: rect testDirtyRect:YES];
828     [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
829
830     [self _setAsideSubviews];
831     [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect
832         rectIsVisibleRectForView:visibleView topView:topView];
833     [self _restoreSubviews];
834
835     if (wasInPrintingMode != isPrinting) {
836         if (wasInPrintingMode) {
837             [self _web_setPrintingModeRecursive];
838         } else {
839             [self _web_clearPrintingModeRecursive];
840         }
841     }
842 }
843
844 // Don't let AppKit even draw subviews. We take care of that.
845 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
846 {
847     BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
848
849     BOOL wasInPrintingMode = _private->printing;
850     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
851
852     if (needToSetAsideSubviews) {
853         // This helps when we print as part of a larger print process.
854         // If the WebHTMLView itself is what we're printing, then we will never have to do this.
855         if (wasInPrintingMode != isPrinting) {
856             if (isPrinting) {
857                 [self _web_setPrintingModeRecursive];
858             } else {
859                 [self _web_clearPrintingModeRecursive];
860             }
861         }
862
863         NSRect boundsBeforeLayout = [self bounds];
864         [self _web_layoutIfNeededRecursive: visRect testDirtyRect:NO];
865
866         // If layout changes the view's bounds, then we need to recompute the visRect.
867         // That's because the visRect passed to us was based on the bounds at the time
868         // we were called. This method is only displayed to draw "all", so it's safe
869         // to just call visibleRect to compute the entire rectangle.
870         if (!NSEqualRects(boundsBeforeLayout, [self bounds])) {
871             visRect = [self visibleRect];
872         }
873
874         [self _setAsideSubviews];
875     }
876     
877     [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
878     
879     if (needToSetAsideSubviews) {
880         if (wasInPrintingMode != isPrinting) {
881             if (wasInPrintingMode) {
882                 [self _web_setPrintingModeRecursive];
883             } else {
884                 [self _web_clearPrintingModeRecursive];
885             }
886         }
887
888         [self _restoreSubviews];
889     }
890 }
891
892 - (WebHTMLView *)_topHTMLView
893 {
894     WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
895     ASSERT([view isKindOfClass:[WebHTMLView class]]);
896     return view;
897 }
898
899 - (BOOL)_isTopHTMLView
900 {
901     return self == [self _topHTMLView];
902 }
903
904 - (BOOL)_insideAnotherHTMLView
905 {
906     return self != [self _topHTMLView];
907 }
908
909 - (void)scrollPoint:(NSPoint)point
910 {
911     // Since we can't subclass NSTextView to do what we want, we have to second guess it here.
912     // If we get called during the handling of a key down event, we assume the call came from
913     // NSTextView, and ignore it and use our own code to decide how to page up and page down
914     // We are smarter about how far to scroll, and we have "superview scrolling" logic.
915     NSEvent *event = [[self window] currentEvent];
916     if ([event type] == NSKeyDown) {
917         const unichar pageUp = NSPageUpFunctionKey;
918         if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageUp length:1]].length == 1) {
919             [self tryToPerform:@selector(scrollPageUp:) with:nil];
920             return;
921         }
922         const unichar pageDown = NSPageDownFunctionKey;
923         if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageDown length:1]].length == 1) {
924             [self tryToPerform:@selector(scrollPageDown:) with:nil];
925             return;
926         }
927     }
928     
929     [super scrollPoint:point];
930 }
931
932 - (NSView *)hitTest:(NSPoint)point
933 {
934     // WebHTMLView objects handle all events for objects inside them.
935     // To get those events, we prevent hit testing from AppKit.
936
937     // But there are three exceptions to this:
938     //   1) For right mouse clicks and control clicks we don't yet have an implementation
939     //      that works for nested views, so we let the hit testing go through the
940     //      standard NSView code path (needs to be fixed, see bug 4361618).
941     //   2) Java depends on doing a hit test inside it's mouse moved handling,
942     //      so we let the hit testing go through the standard NSView code path
943     //      when the current event is a mouse move (except when we are calling
944     //      from _updateMouseoverWithEvent, so we have to use a global,
945     //      forceWebHTMLViewHitTest, for that)
946     //   3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
947     //      both need to figure out which view to check with inside the WebHTMLView.
948     //      They use a global to change the behavior of hitTest: so they can get the
949     //      right view. The global is forceNSViewHitTest and the method they use to
950     //      do the hit testing is _hitViewForEvent:. (But this does not work correctly
951     //      when there is HTML overlapping the view, see bug 4361626)
952     //   4) NSAccessibilityHitTest relies on this for checking the cursor position.
953     //      Our check for that is whether the event is NSFlagsChanged.  This works
954     //      for VoiceOver's cntl-opt-f5 command (move focus to item under cursor)
955     //      and Dictionary's cmd-cntl-D (open dictionary popup for item under cursor).
956     //      This is of course a hack.
957
958     BOOL captureHitsOnSubviews;
959     if (forceNSViewHitTest)
960         captureHitsOnSubviews = NO;
961     else if (forceWebHTMLViewHitTest)
962         captureHitsOnSubviews = YES;
963     else {
964         NSEvent *event = [[self window] currentEvent];
965         captureHitsOnSubviews = !([event type] == NSMouseMoved
966             || [event type] == NSRightMouseDown
967             || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
968             || [event type] == NSFlagsChanged);
969     }
970
971     if (!captureHitsOnSubviews)
972         return [super hitTest:point];
973     if ([[self superview] mouse:point inRect:[self frame]])
974         return self;
975     return nil;
976 }
977
978 static WebHTMLView *lastHitView = nil;
979
980 - (void)_clearLastHitViewIfSelf
981 {
982     if (lastHitView == self) {
983         lastHitView = nil;
984     }
985 }
986
987 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
988 {
989     ASSERT(_private->trackingRectOwner == nil);
990     _private->trackingRectOwner = owner;
991     _private->trackingRectUserData = data;
992     return TRACKING_RECT_TAG;
993 }
994
995 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
996 {
997     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
998     ASSERT(_private->trackingRectOwner == nil);
999     _private->trackingRectOwner = owner;
1000     _private->trackingRectUserData = data;
1001     return TRACKING_RECT_TAG;
1002 }
1003
1004 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1005 {
1006     ASSERT(count == 1);
1007     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1008     ASSERT(_private->trackingRectOwner == nil);
1009     _private->trackingRectOwner = owner;
1010     _private->trackingRectUserData = userDataList[0];
1011     trackingNums[0] = TRACKING_RECT_TAG;
1012 }
1013
1014 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1015 {
1016     if (tag == 0)
1017         return;
1018     
1019     if (_private && (tag == TRACKING_RECT_TAG)) {
1020         _private->trackingRectOwner = nil;
1021         return;
1022     }
1023     
1024     if (_private && (tag == _private->lastToolTipTag)) {
1025         [super removeTrackingRect:tag];
1026         _private->lastToolTipTag = 0;
1027         return;
1028     }
1029     
1030     // If any other tracking rect is being removed, we don't know how it was created
1031     // and it's possible there's a leak involved (see 3500217)
1032     ASSERT_NOT_REACHED();
1033 }
1034
1035 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1036 {
1037     int i;
1038     for (i = 0; i < count; ++i) {
1039         int tag = tags[i];
1040         if (tag == 0)
1041             continue;
1042         ASSERT(tag == TRACKING_RECT_TAG);
1043         if (_private != nil) {
1044             _private->trackingRectOwner = nil;
1045         }
1046     }
1047 }
1048
1049 - (void)_sendToolTipMouseExited
1050 {
1051     // Nothing matters except window, trackingNumber, and userData.
1052     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1053         location:NSMakePoint(0, 0)
1054         modifierFlags:0
1055         timestamp:0
1056         windowNumber:[[self window] windowNumber]
1057         context:NULL
1058         eventNumber:0
1059         trackingNumber:TRACKING_RECT_TAG
1060         userData:_private->trackingRectUserData];
1061     [_private->trackingRectOwner mouseExited:fakeEvent];
1062 }
1063
1064 - (void)_sendToolTipMouseEntered
1065 {
1066     // Nothing matters except window, trackingNumber, and userData.
1067     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1068         location:NSMakePoint(0, 0)
1069         modifierFlags:0
1070         timestamp:0
1071         windowNumber:[[self window] windowNumber]
1072         context:NULL
1073         eventNumber:0
1074         trackingNumber:TRACKING_RECT_TAG
1075         userData:_private->trackingRectUserData];
1076     [_private->trackingRectOwner mouseEntered:fakeEvent];
1077 }
1078
1079 - (void)_setToolTip:(NSString *)string
1080 {
1081     NSString *toolTip = [string length] == 0 ? nil : string;
1082     NSString *oldToolTip = _private->toolTip;
1083     if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1084         return;
1085     }
1086     if (oldToolTip) {
1087         [self _sendToolTipMouseExited];
1088         [oldToolTip release];
1089     }
1090     _private->toolTip = [toolTip copy];
1091     if (toolTip) {
1092         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1093         [self removeAllToolTips];
1094         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1095         _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1096         [self _sendToolTipMouseEntered];
1097     }
1098 }
1099
1100 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1101 {
1102     return [[_private->toolTip copy] autorelease];
1103 }
1104
1105 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1106 {
1107     if (_private->closed)
1108         return;
1109
1110     NSView *contentView = [[event window] contentView];
1111     NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1112     
1113     forceWebHTMLViewHitTest = YES;
1114     NSView *hitView = [contentView hitTest:locationForHitTest];
1115     forceWebHTMLViewHitTest = NO;
1116     
1117     WebHTMLView *view = nil;
1118     if ([hitView isKindOfClass:[WebHTMLView class]]) 
1119         view = (WebHTMLView *)hitView; 
1120
1121     if (view)
1122         [view retain];
1123
1124     if (lastHitView != view && lastHitView != nil) {
1125         // If we are moving out of a view (or frame), let's pretend the mouse moved
1126         // all the way out of that view. But we have to account for scrolling, because
1127         // khtml doesn't understand our clipping.
1128         NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1129         float yScroll = visibleRect.origin.y;
1130         float xScroll = visibleRect.origin.x;
1131
1132         event = [NSEvent mouseEventWithType:NSMouseMoved
1133                          location:NSMakePoint(-1 - xScroll, -1 - yScroll )
1134                          modifierFlags:[[NSApp currentEvent] modifierFlags]
1135                          timestamp:[NSDate timeIntervalSinceReferenceDate]
1136                          windowNumber:[[view window] windowNumber]
1137                          context:[[NSApp currentEvent] context]
1138                          eventNumber:0 clickCount:0 pressure:0];
1139         [[lastHitView _bridge] mouseMoved:event];
1140     }
1141
1142     lastHitView = view;
1143
1144     if (view) {
1145         [[view _bridge] mouseMoved:event];
1146
1147         NSPoint point = [view convertPoint:[event locationInWindow] fromView:nil];
1148         NSDictionary *element = [view elementAtPoint:point];
1149
1150         // Have the web view send a message to the delegate so it can do status bar display.
1151         [[view _webView] _mouseDidMoveOverElement:element modifierFlags:[event modifierFlags]];
1152
1153         // Set a tool tip; it won't show up right away but will if the user pauses.
1154         NSString *newToolTip = nil;
1155         if (_private->showsURLsInToolTips) {
1156             DOMHTMLElement *domElement = [element objectForKey:WebElementDOMNodeKey];
1157             if ([domElement isKindOfClass:[DOMHTMLInputElement class]]) {
1158                 if ([[(DOMHTMLInputElement *)domElement type] isEqualToString:@"submit"])
1159                     newToolTip = [[(DOMHTMLInputElement *) domElement form] action];
1160             }
1161             if (newToolTip == nil)
1162                 newToolTip = [[element objectForKey:WebElementLinkURLKey] _web_userVisibleString];
1163         }
1164         if (newToolTip == nil)
1165             newToolTip = [element objectForKey:WebElementTitleKey];
1166         [view _setToolTip:newToolTip];
1167
1168         [view release];
1169     }
1170 }
1171
1172 + (NSArray *)_insertablePasteboardTypes
1173 {
1174     static NSArray *types = nil;
1175     if (!types) {
1176         types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType,
1177             NSFilenamesPboardType, NSTIFFPboardType, NSPICTPboardType, NSURLPboardType, 
1178             NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, nil];
1179     }
1180     return types;
1181 }
1182
1183 + (NSArray *)_selectionPasteboardTypes
1184 {
1185     // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1186     return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1187 }
1188
1189 - (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
1190 {
1191     NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
1192
1193     BOOL drawURLString = YES;
1194     BOOL clipURLString = NO, clipLabelString = NO;
1195     
1196     NSString *label = [element objectForKey: WebElementLinkLabelKey];
1197     NSString *urlString = [linkURL _web_userVisibleString];
1198     
1199     if (!label) {
1200         drawURLString = NO;
1201         label = urlString;
1202     }
1203     
1204     NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
1205                                                    toHaveTrait:NSBoldFontMask];
1206     NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
1207     NSSize labelSize;
1208     labelSize.width = [label _web_widthWithFont: labelFont];
1209     labelSize.height = [labelFont ascender] - [labelFont descender];
1210     if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
1211         labelSize.width = MAX_DRAG_LABEL_WIDTH;
1212         clipLabelString = YES;
1213     }
1214     
1215     NSSize imageSize, urlStringSize;
1216     imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2;
1217     imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2;
1218     if (drawURLString) {
1219         urlStringSize.width = [urlString _web_widthWithFont: urlFont];
1220         urlStringSize.height = [urlFont ascender] - [urlFont descender];
1221         imageSize.height += urlStringSize.height;
1222         if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
1223             imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
1224             clipURLString = YES;
1225         } else {
1226             imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2, urlStringSize.width + DRAG_LABEL_BORDER_X * 2);
1227         }
1228     }
1229     NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
1230     [dragImage lockFocus];
1231     
1232     [[NSColor colorWithCalibratedRed: 0.7 green: 0.7 blue: 0.7 alpha: 0.8] set];
1233     
1234     // Drag a rectangle with rounded corners/
1235     NSBezierPath *path = [NSBezierPath bezierPath];
1236     [path appendBezierPathWithOvalInRect: NSMakeRect(0,0, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
1237     [path appendBezierPathWithOvalInRect: NSMakeRect(0,imageSize.height - DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
1238     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2, imageSize.height - DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
1239     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2,0, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
1240     
1241     [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0, imageSize.width - DRAG_LABEL_RADIUS * 2, imageSize.height)];
1242     [path appendBezierPathWithRect: NSMakeRect(0, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10, imageSize.height - 2 * DRAG_LABEL_RADIUS)];
1243     [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS - 20,DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 20, imageSize.height - 2 * DRAG_LABEL_RADIUS)];
1244     [path fill];
1245         
1246     NSColor *topColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.75];
1247     NSColor *bottomColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.5];
1248     if (drawURLString) {
1249         if (clipURLString)
1250             urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2) withFont:urlFont];
1251
1252         [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender]) 
1253              withTopColor:topColor bottomColor:bottomColor font:urlFont];
1254     }
1255
1256     if (clipLabelString)
1257         label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2) withFont:labelFont];
1258     [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
1259              withTopColor:topColor bottomColor:bottomColor font:labelFont];
1260     
1261     [dragImage unlockFocus];
1262     
1263     return dragImage;
1264 }
1265
1266 - (BOOL)_startDraggingImage:(NSImage *)wcDragImage at:(NSPoint)wcDragLoc operation:(NSDragOperation)op event:(NSEvent *)mouseDraggedEvent sourceIsDHTML:(BOOL)srcIsDHTML DHTMLWroteData:(BOOL)dhtmlWroteData
1267 {
1268     WebHTMLView *topHTMLView = [self _topHTMLView];
1269     if (self != topHTMLView)
1270         return [topHTMLView _startDraggingImage:wcDragImage at:wcDragLoc operation:op event:mouseDraggedEvent sourceIsDHTML:srcIsDHTML DHTMLWroteData:dhtmlWroteData];
1271
1272     NSPoint mouseDownPoint = [self convertPoint:[_private->mouseDownEvent locationInWindow] fromView:nil];
1273     NSDictionary *element = [self elementAtPoint:mouseDownPoint allowShadowContent:YES];
1274
1275     NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
1276     NSURL *imageURL = [element objectForKey:WebElementImageURLKey];
1277     BOOL isSelected = [[element objectForKey:WebElementIsSelectedKey] boolValue];
1278
1279     [_private->draggingImageURL release];
1280     _private->draggingImageURL = nil;
1281
1282     NSPoint mouseDraggedPoint = [self convertPoint:[mouseDraggedEvent locationInWindow] fromView:nil];
1283     _private->webCoreDragOp = op;     // will be DragNone if WebCore doesn't care
1284     NSImage *dragImage = nil;
1285     NSPoint dragLoc = { 0, 0 }; // quiet gcc 4.0 warning
1286
1287     // We allow WebCore to override the drag image, even if its a link, image or text we're dragging.
1288     // This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp.
1289     // We could verify that ActionDHTML is allowed, although WebCore does claim to respect the action.
1290     if (wcDragImage) {
1291         dragImage = wcDragImage;
1292         // wcDragLoc is the cursor position relative to the lower-left corner of the image.
1293         // We add in the Y dimension because we are a flipped view, so adding moves the image down.
1294         if (linkURL)
1295             // see HACK below
1296             dragLoc = NSMakePoint(mouseDraggedPoint.x - wcDragLoc.x, mouseDraggedPoint.y + wcDragLoc.y);
1297         else
1298             dragLoc = NSMakePoint(mouseDownPoint.x - wcDragLoc.x, mouseDownPoint.y + wcDragLoc.y);
1299         _private->dragOffset = wcDragLoc;
1300     }
1301     
1302     WebView *webView = [self _webView];
1303     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1304     BOOL startedDrag = YES;  // optimism - we almost always manage to start the drag
1305
1306     // note per kwebster, the offset arg below is always ignored in positioning the image
1307     DOMNode *node = [element objectForKey:WebElementDOMNodeKey];
1308     WebHTMLView *innerHTMLView = (WebHTMLView *)[[[[node ownerDocument] webFrame] frameView] documentView];
1309     ASSERT([innerHTMLView isKindOfClass:[WebHTMLView class]]);
1310     if (imageURL != nil
1311             && [node isKindOfClass:[DOMElement class]]
1312             && [(DOMElement *)node image]
1313             && (_private->dragSourceActionMask & WebDragSourceActionImage)) {
1314         id source = self;
1315         if (!dhtmlWroteData) {
1316             // Select the image when it is dragged. This allows the image to be moved via MoveSelectionCommandImpl and this matches NSTextView's behavior.
1317             ASSERT(node != nil);
1318             [webView setSelectedDOMRange:[[node ownerDocument] _createRangeWithNode:node] affinity:NSSelectionAffinityDownstream];
1319             _private->draggingImageURL = [imageURL retain];
1320             
1321             WebArchive *archive;
1322             
1323             // If the image element comes from an ImageDocument, we don't want to 
1324             // create a web archive from the image element.
1325             if ([[self _bridge] canSaveAsWebArchive])
1326                 archive = [node webArchive];
1327             else
1328                 archive = [WebArchiver archiveMainResourceForFrame:[self _frame]];
1329             
1330             source = [pasteboard _web_declareAndWriteDragImageForElement:(DOMElement *)node
1331                                                                      URL:linkURL ? linkURL : imageURL
1332                                                                    title:[element objectForKey:WebElementImageAltStringKey]
1333                                                                  archive:archive
1334                                                                   source:self];
1335         }
1336         [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionImage fromPoint:mouseDownPoint withPasteboard:pasteboard];
1337         if (dragImage == nil)
1338             [self _web_DragImageForElement:(DOMElement *)node
1339                                       rect:[self convertRect:[[element objectForKey:WebElementImageRectKey] rectValue] fromView:innerHTMLView]
1340                                      event:_private->mouseDownEvent
1341                                 pasteboard:pasteboard
1342                                     source:source
1343                                     offset:&_private->dragOffset];
1344         else
1345             [self dragImage:dragImage
1346                          at:dragLoc
1347                      offset:NSZeroSize
1348                       event:_private->mouseDownEvent
1349                  pasteboard:pasteboard
1350                      source:source
1351                   slideBack:YES];
1352     } else if (linkURL && (_private->dragSourceActionMask & WebDragSourceActionLink)) {
1353         if (!dhtmlWroteData) {
1354             NSArray *types = [NSPasteboard _web_writableTypesForURL];
1355             [pasteboard declareTypes:types owner:self];
1356             [pasteboard _web_writeURL:linkURL andTitle:[element objectForKey:WebElementLinkLabelKey] types:types];            
1357         }
1358         [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionLink fromPoint:mouseDownPoint withPasteboard:pasteboard];
1359         if (dragImage == nil) {
1360             dragImage = [self _dragImageForLinkElement:element];
1361             NSSize offset = NSMakeSize([dragImage size].width / 2, -DRAG_LABEL_BORDER_Y);
1362             dragLoc = NSMakePoint(mouseDraggedPoint.x - offset.width, mouseDraggedPoint.y - offset.height);
1363             _private->dragOffset.x = offset.width;
1364             _private->dragOffset.y = -offset.height;        // inverted because we are flipped
1365         }
1366         // HACK:  We should pass the mouseDown event instead of the mouseDragged!  This hack gets rid of
1367         // a flash of the image at the mouseDown location when the drag starts.
1368         [self dragImage:dragImage
1369                      at:dragLoc
1370                  offset:NSZeroSize
1371                   event:mouseDraggedEvent
1372              pasteboard:pasteboard
1373                  source:self
1374               slideBack:YES];
1375     } else if (isSelected && (_private->dragSourceActionMask & WebDragSourceActionSelection)) {
1376         if (!dhtmlWroteData)
1377             [innerHTMLView _writeSelectionToPasteboard:pasteboard];
1378         [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionSelection fromPoint:mouseDownPoint withPasteboard:pasteboard];
1379         if (dragImage == nil) {
1380             dragImage = [innerHTMLView _selectionDraggingImage];
1381             NSRect draggingRect = [self convertRect:[innerHTMLView _selectionDraggingRect] fromView:innerHTMLView];
1382             dragLoc = NSMakePoint(NSMinX(draggingRect), NSMaxY(draggingRect));
1383             _private->dragOffset.x = mouseDownPoint.x - dragLoc.x;
1384             _private->dragOffset.y = dragLoc.y - mouseDownPoint.y;        // inverted because we are flipped
1385         }
1386         [self dragImage:dragImage
1387                      at:dragLoc
1388                  offset:NSZeroSize
1389                   event:_private->mouseDownEvent
1390              pasteboard:pasteboard
1391                  source:self
1392               slideBack:YES];
1393     } else if (srcIsDHTML) {
1394         ASSERT(_private->dragSourceActionMask & WebDragSourceActionDHTML);
1395         [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionDHTML fromPoint:mouseDownPoint withPasteboard:pasteboard];
1396         if (dragImage == nil) {
1397             // WebCore should have given us an image, but we'll make one up
1398             // FIXME: Oops! I removed this image from WebKit. Is this a dead code path?
1399             NSString *imagePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"missing_image" ofType:@"tiff"];
1400             dragImage = [[[NSImage alloc] initWithContentsOfFile:imagePath] autorelease];
1401             NSSize imageSize = [dragImage size];
1402             dragLoc = NSMakePoint(mouseDownPoint.x - imageSize.width / 2, mouseDownPoint.y + imageSize.height / 2);
1403             _private->dragOffset.x = imageSize.width / 2;
1404             _private->dragOffset.y = imageSize.height / 2;
1405         }
1406         [self dragImage:dragImage
1407                      at:dragLoc
1408                  offset:NSZeroSize
1409                   event:_private->mouseDownEvent
1410              pasteboard:pasteboard
1411                  source:self
1412               slideBack:YES];
1413     } else {
1414         // Only way I know if to get here is if the original element clicked on in the mousedown is no longer
1415         // under the mousedown point, so linkURL, imageURL and isSelected are all false/nil.
1416         startedDrag = NO;
1417     }
1418     return startedDrag;
1419 }
1420
1421 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
1422 {
1423     [self autoscroll:event];
1424     [self _startAutoscrollTimer:event];
1425 }
1426
1427 - (BOOL)_mayStartDragAtEventLocation:(NSPoint)location
1428 {
1429     WebHTMLView *topHTMLView = [self _topHTMLView];
1430     if (self != topHTMLView)
1431         return [topHTMLView _mayStartDragAtEventLocation:location];
1432
1433     NSPoint mouseDownPoint = [self convertPoint:location fromView:nil];
1434     NSDictionary *mouseDownElement = [self elementAtPoint:mouseDownPoint allowShadowContent:YES];
1435
1436     ASSERT([self _webView]);
1437
1438     if ([mouseDownElement objectForKey:WebElementImageKey]
1439             && [mouseDownElement objectForKey:WebElementImageURLKey]
1440             && [[[self _webView] preferences] loadsImagesAutomatically]
1441             && (_private->dragSourceActionMask & WebDragSourceActionImage))
1442         return YES;
1443     
1444     if ([mouseDownElement objectForKey:WebElementLinkURLKey]
1445             && (_private->dragSourceActionMask & WebDragSourceActionLink))
1446         return YES;
1447     
1448     if ([[mouseDownElement objectForKey:WebElementIsSelectedKey] boolValue]
1449             && (_private->dragSourceActionMask & WebDragSourceActionSelection))
1450         return YES;
1451     
1452     return NO;
1453 }
1454
1455 - (WebPluginController *)_pluginController
1456 {
1457     return _private->pluginController;
1458 }
1459
1460 - (void)_web_setPrintingModeRecursive
1461 {
1462     [self _setPrinting:YES minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
1463     [super _web_setPrintingModeRecursive];
1464 }
1465
1466 - (void)_web_clearPrintingModeRecursive
1467 {
1468     [self _setPrinting:NO minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
1469     [super _web_clearPrintingModeRecursive];
1470 }
1471
1472 - (void)_layoutIfNeeded
1473 {
1474     ASSERT(!_private->subviewsSetAside);
1475
1476     if ([[self _bridge] needsLayout])
1477         _private->needsLayout = YES;
1478     if (_private->needsToApplyStyles || _private->needsLayout)
1479         [self layout];
1480 }
1481
1482 - (void)_web_layoutIfNeededRecursive
1483 {
1484     [self _layoutIfNeeded];
1485     [super _web_layoutIfNeededRecursive];
1486 }
1487
1488 - (void)_web_layoutIfNeededRecursive:(NSRect)displayRect testDirtyRect:(bool)testDirtyRect
1489 {
1490     ASSERT(!_private->subviewsSetAside);
1491
1492     displayRect = NSIntersectionRect(displayRect, [self bounds]);
1493     if (testDirtyRect) {
1494         NSRect dirtyRect = [self _dirtyRect];
1495         displayRect = NSIntersectionRect(displayRect, dirtyRect);
1496     }
1497     if (!NSIsEmptyRect(displayRect))
1498         [self _layoutIfNeeded];
1499
1500     [super _web_layoutIfNeededRecursive:displayRect testDirtyRect:NO];
1501 }
1502
1503 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1504 {
1505     if (_private->autoscrollTimer == nil) {
1506         _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1507             target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1508         _private->autoscrollTriggerEvent = [triggerEvent retain];
1509     }
1510 }
1511
1512 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1513 // We can't remove this yet because it's still in use by Mail.
1514 - (NSRect)_selectionRect
1515 {
1516     return [self selectionRect];
1517 }
1518
1519 - (void)_stopAutoscrollTimer
1520 {
1521     NSTimer *timer = _private->autoscrollTimer;
1522     _private->autoscrollTimer = nil;
1523     [_private->autoscrollTriggerEvent release];
1524     _private->autoscrollTriggerEvent = nil;
1525     [timer invalidate];
1526     [timer release];
1527 }
1528
1529 - (void)_autoscroll
1530 {
1531     // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1532     // a mouse up event.
1533     BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);   
1534     if (!isStillDown){
1535         [self _stopAutoscrollTimer];
1536         return;
1537     }
1538
1539     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1540         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1541         modifierFlags:[[NSApp currentEvent] modifierFlags]
1542         timestamp:[NSDate timeIntervalSinceReferenceDate]
1543         windowNumber:[[self window] windowNumber]
1544         context:[[NSApp currentEvent] context]
1545         eventNumber:0 clickCount:0 pressure:0];
1546     [self mouseDragged:fakeEvent];
1547 }
1548
1549 - (BOOL)_canCopy
1550 {
1551     // Copying can be done regardless of whether you can edit.
1552     return [self _hasSelection];
1553 }
1554
1555 - (BOOL)_canCut
1556 {
1557     return [self _hasSelection] && [self _isEditable];
1558 }
1559
1560 - (BOOL)_canDelete
1561 {
1562     return [self _hasSelection] && [self _isEditable];
1563 }
1564
1565 - (BOOL)_canPaste
1566 {
1567     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1568 }
1569
1570 - (BOOL)_canEdit
1571 {
1572     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1573 }
1574
1575 - (BOOL)_canEditRichly
1576 {
1577     return [self _canEdit] && [[self _bridge] isSelectionRichlyEditable];
1578 }
1579
1580 - (BOOL)_canAlterCurrentSelection
1581 {
1582     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1583 }
1584
1585 - (BOOL)_hasSelection
1586 {
1587     return [[self _bridge] selectionState] == WebSelectionStateRange;
1588 }
1589
1590 - (BOOL)_hasSelectionOrInsertionPoint
1591 {
1592     return [[self _bridge] selectionState] != WebSelectionStateNone;
1593 }
1594
1595 - (BOOL)_hasInsertionPoint
1596 {
1597     return [[self _bridge] selectionState] == WebSelectionStateCaret;
1598 }
1599
1600 - (BOOL)_isEditable
1601 {
1602     return [[self _webView] isEditable] || [[self _bridge] isSelectionEditable];
1603 }
1604
1605 - (BOOL)_isSelectionMisspelled
1606 {
1607     NSString *selectedString = [self selectedString];
1608     unsigned length = [selectedString length];
1609     if (length == 0) {
1610         return NO;
1611     }
1612     NSRange range = [[NSSpellChecker sharedSpellChecker] checkSpellingOfString:selectedString
1613                                                                     startingAt:0
1614                                                                       language:nil
1615                                                                           wrap:NO
1616                                                         inSpellDocumentWithTag:[[self _webView] spellCheckerDocumentTag]
1617                                                                      wordCount:NULL];
1618     return range.length == length;
1619 }
1620
1621 - (NSArray *)_guessesForMisspelledSelection
1622 {
1623     ASSERT([[self selectedString] length] != 0);
1624     return [[NSSpellChecker sharedSpellChecker] guessesForWord:[self selectedString]];
1625 }
1626
1627 - (void)_changeSpellingFromMenu:(id)sender
1628 {
1629     ASSERT([[self selectedString] length] != 0);
1630     if ([self _shouldReplaceSelectionWithText:[sender title] givenAction:WebViewInsertActionPasted]) {
1631         [[self _bridge] replaceSelectionWithText:[sender title] selectReplacement:YES smartReplace:NO];
1632     }
1633 }
1634
1635 - (void)_ignoreSpellingFromMenu:(id)sender
1636 {
1637     ASSERT([[self selectedString] length] != 0);
1638     [[NSSpellChecker sharedSpellChecker] ignoreWord:[self selectedString] inSpellDocumentWithTag:[[self _webView] spellCheckerDocumentTag]];
1639 }
1640
1641 - (void)_learnSpellingFromMenu:(id)sender
1642 {
1643     ASSERT([[self selectedString] length] != 0);
1644     [[NSSpellChecker sharedSpellChecker] learnWord:[self selectedString]];
1645 }
1646
1647 - (void)_lookUpInDictionaryFromMenu:(id)sender
1648 {
1649     // This should only be called when there's a selection, but play it safe.
1650     if (![self _hasSelection]) {
1651         return;
1652     }
1653     
1654     // Soft link to dictionary-display function to avoid linking another framework (ApplicationServices/LangAnalysis)
1655     static OSStatus (*__dictionaryServiceWindowShow)(id inWordString, NSRect inWordBoundary, UInt16 inLineDirection) = NULL;
1656     static const struct mach_header *frameworkImageHeader = NULL;
1657     static BOOL lookedForFunction = NO;
1658     if (!lookedForFunction) {
1659         __dictionaryServiceWindowShow = _NSSoftLinkingGetFrameworkFuncPtr(@"ApplicationServices", @"LangAnalysis", "_DCMDictionaryServiceWindowShow", &frameworkImageHeader);
1660         lookedForFunction = YES;
1661     }
1662     if (!__dictionaryServiceWindowShow) {
1663         LOG_ERROR("Couldn't find _DCMDictionaryServiceWindowShow"); 
1664         return;
1665     }
1666     
1667     // FIXME: must check for right-to-left here
1668     NSWritingDirection writingDirection = NSWritingDirectionLeftToRight;
1669     
1670     NSAttributedString *attrString = [self selectedAttributedString];
1671     // FIXME: the dictionary API expects the rect for the first line of selection. Passing
1672     // the rect for the entire selection, as we do here, positions the pop-up window near
1673     // the bottom of the selection rather than at the selected word.
1674     NSRect rect = [self convertRect:[[self _bridge] visibleSelectionRect] toView:nil];
1675     rect.origin = [[self window] convertBaseToScreen:rect.origin];
1676     NSData *data = [attrString RTFFromRange:NSMakeRange(0, [attrString length]) documentAttributes:nil];
1677     (void)__dictionaryServiceWindowShow(data, rect, (writingDirection == NSWritingDirectionRightToLeft) ? 1 : 0);
1678 }
1679
1680 - (BOOL)_transparentBackground
1681 {
1682     return _private->transparentBackground;
1683 }
1684
1685 - (void)_setTransparentBackground:(BOOL)f
1686 {
1687     _private->transparentBackground = f;
1688 }
1689
1690 - (NSImage *)_selectionDraggingImage
1691 {
1692     if ([self _hasSelection]) {
1693         NSImage *dragImage = [[self _bridge] selectionImageForcingWhiteText:NO];
1694         [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1695         return dragImage;
1696     }
1697     return nil;
1698 }
1699
1700 - (NSRect)_selectionDraggingRect
1701 {
1702     // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1703     return [self selectionImageRect];
1704 }
1705
1706 - (BOOL)_canIncreaseSelectionListLevel
1707 {
1708     return ([self _canEditRichly] && [[self _bridge] canIncreaseSelectionListLevel]);
1709 }
1710
1711 - (BOOL)_canDecreaseSelectionListLevel
1712 {
1713     return ([self _canEditRichly] && [[self _bridge] canDecreaseSelectionListLevel]);
1714 }
1715
1716 - (DOMNode *)_increaseSelectionListLevel
1717 {
1718     if (![self _canEditRichly])
1719         return nil;
1720         
1721     WebFrameBridge *bridge = [self _bridge];
1722     return [bridge increaseSelectionListLevel];
1723 }
1724
1725 - (DOMNode *)_increaseSelectionListLevelOrdered
1726 {
1727     if (![self _canEditRichly])
1728         return nil;
1729         
1730     WebFrameBridge *bridge = [self _bridge];
1731     return [bridge increaseSelectionListLevelOrdered];
1732 }
1733
1734 - (DOMNode *)_increaseSelectionListLevelUnordered
1735 {
1736     if (![self _canEditRichly])
1737         return nil;
1738         
1739     WebFrameBridge *bridge = [self _bridge];
1740     return [bridge increaseSelectionListLevelUnordered];
1741 }
1742
1743 - (void)_decreaseSelectionListLevel
1744 {
1745     if (![self _canEditRichly])
1746         return;
1747         
1748     WebFrameBridge *bridge = [self _bridge];
1749     [bridge decreaseSelectionListLevel];
1750 }
1751
1752 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1753 {
1754     if (!_private->highlighters)
1755         _private->highlighters = [[NSMutableDictionary alloc] init];
1756     [_private->highlighters setObject:highlighter forKey:type];
1757 }
1758
1759 - (void)_removeHighlighterOfType:(NSString*)type
1760 {
1761     [_private->highlighters removeObjectForKey:type];
1762 }
1763
1764 - (BOOL)_web_firstResponderCausesFocusDisplay
1765 {
1766     return [self _web_firstResponderIsSelfOrDescendantView] || [[self window] firstResponder] == [self _frameView];
1767 }
1768
1769 - (void)_updateActiveState
1770 {
1771     // This method does the job of updating the view based on the view's firstResponder-ness and
1772     // the window key-ness of the window containing this view. This involves four kinds of 
1773     // drawing updates right now, all handled in WebCore in response to the call over the bridge. 
1774     // 
1775     // The four display attributes are as follows:
1776     // 
1777     // 1. The background color used to draw behind selected content (active | inactive color)
1778     // 2. Caret blinking (blinks | does not blink)
1779     // 3. The drawing of a focus ring around links in web pages.
1780     // 4. Changing the tint of controls from clear to aqua/graphite and vice versa
1781     //
1782     // Also, this is responsible for letting the bridge know if the window has gained or lost focus
1783     // so we can send focus and blur events.
1784
1785     NSWindow *window = [self window];
1786     BOOL windowIsKey = [window isKeyWindow];
1787     BOOL windowOrSheetIsKey = windowIsKey || [[window attachedSheet] isKeyWindow];
1788
1789     BOOL isActive = !_private->resigningFirstResponder && windowIsKey && (_private->descendantBecomingFirstResponder || [self _web_firstResponderCausesFocusDisplay]);
1790     
1791     [[self _bridge] setWindowHasFocus:windowOrSheetIsKey];
1792     [[self _bridge] setIsActive:isActive];
1793 }
1794
1795 - (unsigned)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag
1796 {
1797     return [[self _bridge] markAllMatchesForText:string caseSensitive:caseFlag];
1798 }
1799
1800 - (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
1801 {
1802     [[self _bridge] setMarkedTextMatchesAreHighlighted:newValue];
1803 }
1804
1805 - (BOOL)markedTextMatchesAreHighlighted
1806 {
1807     return [[self _bridge] markedTextMatchesAreHighlighted];
1808 }
1809
1810 - (void)unmarkAllTextMatches
1811 {
1812     return [[self _bridge] unmarkAllTextMatches];
1813 }
1814
1815 - (NSArray *)rectsForTextMatches
1816 {
1817     return [[self _bridge] rectsForTextMatches];
1818 }
1819
1820 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1821 {
1822     ASSERT([self _hasSelection]);
1823     NSArray *types = [self pasteboardTypesForSelection];
1824
1825     // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1826     NSAttributedString *attributedString = [self selectedAttributedString];
1827     NSMutableArray *mutableTypes = nil;
1828     if (![attributedString containsAttachments]) {
1829         mutableTypes = [types mutableCopy];
1830         [mutableTypes removeObject:NSRTFDPboardType];
1831         types = mutableTypes;
1832     }
1833
1834     [pasteboard declareTypes:types owner:nil];
1835     [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1836     [mutableTypes release];
1837 }
1838
1839 - (void)close
1840 {
1841     if (_private->closed)
1842         return;
1843     [self _clearLastHitViewIfSelf];
1844     // FIXME: This is slow; should remove individual observers instead.
1845     [[NSNotificationCenter defaultCenter] removeObserver:self];
1846     [_private->pluginController destroyAllPlugins];
1847     // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1848     [self removeAllToolTips];
1849     _private->closed = YES;
1850 }
1851
1852 @end
1853
1854 @implementation NSView (WebHTMLViewFileInternal)
1855
1856 - (void)_web_setPrintingModeRecursive
1857 {
1858     [_subviews makeObjectsPerformSelector:@selector(_web_setPrintingModeRecursive)];
1859 }
1860
1861 - (void)_web_clearPrintingModeRecursive
1862 {
1863     [_subviews makeObjectsPerformSelector:@selector(_web_clearPrintingModeRecursive)];
1864 }
1865
1866 - (void)_web_layoutIfNeededRecursive
1867 {
1868     [_subviews makeObjectsPerformSelector:@selector(_web_layoutIfNeededRecursive)];
1869 }
1870
1871 - (void)_web_layoutIfNeededRecursive: (NSRect)rect testDirtyRect:(bool)testDirtyRect
1872 {
1873     unsigned index, count;
1874     for (index = 0, count = [(NSArray *)_subviews count]; index < count; index++) {
1875         NSView *subview = [_subviews objectAtIndex:index];
1876         NSRect dirtiedSubviewRect = [subview convertRect: rect fromView: self];
1877         [subview _web_layoutIfNeededRecursive: dirtiedSubviewRect testDirtyRect:testDirtyRect];
1878     }
1879 }
1880
1881 @end
1882
1883 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
1884
1885 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
1886 {
1887     if (object == nil) {
1888         [self removeObjectForKey:key];
1889     } else {
1890         [self setObject:object forKey:key];
1891     }
1892 }
1893
1894 @end
1895
1896 // The following is a workaround for
1897 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
1898 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
1899 // Since the category will be searched before the real class, we'll prevent the flag from being
1900 // set on the tool tip panel.
1901
1902 @interface NSToolTipPanel : NSPanel
1903 @end
1904
1905 @interface NSToolTipPanel (WebHTMLViewFileInternal)
1906 @end
1907
1908 @implementation NSToolTipPanel (WebHTMLViewFileInternal)
1909
1910 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
1911 {
1912     // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
1913 }
1914
1915 @end
1916
1917 @interface NSArray (WebHTMLView)
1918 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
1919 @end
1920
1921 @implementation WebHTMLView
1922
1923 + (void)initialize
1924 {
1925     [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] 
1926                              returnTypes:[[self class] _insertablePasteboardTypes]];
1927     _NSInitializeKillRing();
1928 }
1929
1930 - (void)_resetCachedWebPreferences:(NSNotification *)ignored
1931 {
1932     WebPreferences *preferences = [[self _webView] preferences];
1933     // Check for nil because we might not yet have an associated webView when this is called
1934     if (preferences == nil) {
1935         preferences = [WebPreferences standardPreferences];
1936     }
1937     _private->showsURLsInToolTips = [preferences showsURLsInToolTips];
1938 }
1939
1940 - (id)initWithFrame:(NSRect)frame
1941 {
1942     self = [super initWithFrame:frame];
1943     if (!self)
1944         return nil;
1945     
1946     // Make all drawing go through us instead of subviews.
1947     if (NSAppKitVersionNumber >= 711) {
1948         [self _setDrawsOwnDescendants:YES];
1949     }
1950     
1951     _private = [[WebHTMLViewPrivate alloc] init];
1952
1953     _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
1954     _private->needsLayout = YES;
1955     [self _resetCachedWebPreferences:nil];
1956     [[NSNotificationCenter defaultCenter] 
1957             addObserver:self selector:@selector(_resetCachedWebPreferences:) 
1958                    name:WebPreferencesChangedNotification object:nil];
1959     
1960     return self;
1961 }
1962
1963 - (void)dealloc
1964 {
1965     // We can't assert that close has already been called because
1966     // this view can be removed from it's superview, even though
1967     // it could be needed later, so close if needed.
1968     [self close];
1969     [_private release];
1970     _private = nil;
1971     [super dealloc];
1972 }
1973
1974 - (void)finalize
1975 {
1976     // We can't assert that close has already been called because
1977     // this view can be removed from it's superview, even though
1978     // it could be needed later, so close if needed.
1979     [self close];
1980     [super finalize];
1981 }
1982
1983 - (IBAction)takeFindStringFromSelection:(id)sender
1984 {
1985     if (![self _hasSelection]) {
1986         NSBeep();
1987         return;
1988     }
1989
1990     [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
1991 }
1992
1993 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
1994 {
1995     [pasteboard declareTypes:types owner:nil];
1996     [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
1997     return YES;
1998 }
1999
2000 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2001 {
2002     if ([[self _bridge] isSelectionRichlyEditable])
2003         [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
2004     else
2005         [self _pasteAsPlainTextWithPasteboard:pasteboard];
2006     return YES;
2007 }
2008
2009 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2010 {
2011     if (sendType != nil && [[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]) {
2012         return self;
2013     } else if (returnType != nil && [[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2014         return self;
2015     }
2016     return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2017 }
2018
2019 - (void)selectAll:(id)sender
2020 {
2021     [self selectAll];
2022 }
2023
2024 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2025 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will us the
2026 // selector centerSelectionInVisibleArea. We'll leave this old selector in place for two reasons:
2027 // (1) compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2028 // might be using the jumpToSelection: selector, and we don't want to break them.
2029 - (void)jumpToSelection:(id)sender
2030 {
2031     [self centerSelectionInVisibleArea:sender];
2032 }
2033
2034 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item 
2035 {
2036     SEL action = [item action];
2037     WebFrameBridge *bridge = [self _bridge];
2038
2039     if (action == @selector(changeBaseWritingDirection:) // FIXME: check menu item based on writing direction
2040             || action == @selector(changeSpelling:)
2041             || action == @selector(_changeSpellingFromMenu:)
2042             || action == @selector(checkSpelling:)
2043             || action == @selector(complete:)
2044             || action == @selector(deleteBackward:)
2045             || action == @selector(deleteBackwardByDecomposingPreviousCharacter:)
2046             || action == @selector(deleteForward:)
2047             || action == @selector(deleteToBeginningOfLine:)
2048             || action == @selector(deleteToBeginningOfParagraph:)
2049             || action == @selector(deleteToEndOfLine:)
2050             || action == @selector(deleteToEndOfParagraph:)
2051             || action == @selector(deleteToMark:)
2052             || action == @selector(deleteWordBackward:)
2053             || action == @selector(deleteWordForward:)
2054             || action == @selector(insertBacktab:)
2055             || action == @selector(insertLineBreak:)
2056             || action == @selector(insertNewline:)
2057             || action == @selector(insertNewlineIgnoringFieldEditor:)
2058             || action == @selector(insertParagraphSeparator:)
2059             || action == @selector(insertTab:)
2060             || action == @selector(insertTabIgnoringFieldEditor:)
2061             || action == @selector(moveBackward:)
2062             || action == @selector(moveBackwardAndModifySelection:)
2063             || action == @selector(moveDown:)
2064             || action == @selector(moveDownAndModifySelection:)
2065             || action == @selector(moveForward:)
2066             || action == @selector(moveForwardAndModifySelection:)
2067             || action == @selector(moveLeft:)
2068             || action == @selector(moveLeftAndModifySelection:)
2069             || action == @selector(moveParagraphBackwardAndModifySelection:)
2070             || action == @selector(moveParagraphForwardAndModifySelection:)
2071             || action == @selector(moveRight:)
2072             || action == @selector(moveRightAndModifySelection:)
2073             || action == @selector(moveToBeginningOfDocument:)
2074             || action == @selector(moveToBeginningOfDocumentAndModifySelection:)
2075             || action == @selector(moveToBeginningOfSentence:)
2076             || action == @selector(moveToBeginningOfSentenceAndModifySelection:)
2077             || action == @selector(moveToBeginningOfLine:)
2078             || action == @selector(moveToBeginningOfLineAndModifySelection:)
2079             || action == @selector(moveToBeginningOfParagraph:)
2080             || action == @selector(moveToBeginningOfParagraphAndModifySelection:)
2081             || action == @selector(moveToEndOfDocument:)
2082             || action == @selector(moveToEndOfDocumentAndModifySelection:)
2083             || action == @selector(moveToEndOfSentence:)
2084             || action == @selector(moveToEndOfSentenceAndModifySelection:)
2085             || action == @selector(moveToEndOfLine:)
2086             || action == @selector(moveToEndOfLineAndModifySelection:)
2087             || action == @selector(moveToEndOfParagraph:)
2088             || action == @selector(moveToEndOfParagraphAndModifySelection:)
2089             || action == @selector(moveUp:)
2090             || action == @selector(moveUpAndModifySelection:)
2091             || action == @selector(moveWordBackward:)
2092             || action == @selector(moveWordBackwardAndModifySelection:)
2093             || action == @selector(moveWordForward:)
2094             || action == @selector(moveWordForwardAndModifySelection:)
2095             || action == @selector(moveWordLeft:)
2096             || action == @selector(moveWordLeftAndModifySelection:)
2097             || action == @selector(moveWordRight:)
2098             || action == @selector(moveWordRightAndModifySelection:)
2099             || action == @selector(pageDown:)
2100             || action == @selector(pageDownAndModifySelection:)
2101             || action == @selector(pageUp:)
2102             || action == @selector(pageUpAndModifySelection:)
2103             || action == @selector(pasteFont:)
2104             || action == @selector(showGuessPanel:)
2105             || action == @selector(toggleBaseWritingDirection:)
2106             || action == @selector(transpose:)
2107             || action == @selector(yank:)
2108             || action == @selector(yankAndSelect:)) {
2109         return [self _canEdit];
2110     } else if (action == @selector(alignCenter:)
2111             || action == @selector(alignLeft:)
2112             || action == @selector(alignJustified:)
2113             || action == @selector(alignRight:)
2114             || action == @selector(changeAttributes:)
2115             || action == @selector(changeColor:)        
2116             || action == @selector(changeFont:)) {
2117         return [self _canEditRichly];
2118     } else if (action == @selector(capitalizeWord:)
2119                || action == @selector(lowercaseWord:)
2120                || action == @selector(uppercaseWord:)) {
2121         return [self _hasSelection] && [self _isEditable];
2122     } else if (action == @selector(centerSelectionInVisibleArea:)
2123                || action == @selector(jumpToSelection:)
2124                || action == @selector(copyFont:)
2125                || action == @selector(setMark:)) {
2126         return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2127     } else if (action == @selector(changeDocumentBackgroundColor:)) {
2128         return [[self _webView] isEditable] && [self _canEditRichly];
2129     } else if (action == @selector(copy:)) {
2130         return [bridge mayDHTMLCopy] || [self _canCopy];
2131     } else if (action == @selector(cut:)) {
2132         return [bridge mayDHTMLCut] || [self _canDelete];
2133     } else if (action == @selector(delete:)) {
2134         return [self _canDelete];
2135     } else if (action == @selector(_ignoreSpellingFromMenu:)
2136             || action == @selector(_learnSpellingFromMenu:)
2137             || action == @selector(takeFindStringFromSelection:)) {
2138         return [self _hasSelection];
2139     } else if (action == @selector(paste:) || action == @selector(pasteAsPlainText:)) {
2140         return [bridge mayDHTMLPaste] || [self _canPaste];
2141     } else if (action == @selector(pasteAsRichText:)) {
2142         return [bridge mayDHTMLPaste] || ([self _canPaste] && [[self _bridge] isSelectionRichlyEditable]);
2143     } else if (action == @selector(performFindPanelAction:)) {
2144         // FIXME: Not yet implemented.
2145         return NO;
2146     } else if (action == @selector(selectToMark:)
2147             || action == @selector(swapWithMark:)) {
2148         return [self _hasSelectionOrInsertionPoint] && [[self _bridge] markDOMRange] != nil;
2149     } else if (action == @selector(subscript:)) {
2150         NSMenuItem *menuItem = (NSMenuItem *)item;
2151         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2152             DOMCSSStyleDeclaration *style = [self _emptyStyle];
2153             [style setVerticalAlign:@"sub"];
2154             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2155         }
2156         return [self _canEditRichly];
2157     } else if (action == @selector(superscript:)) {
2158         NSMenuItem *menuItem = (NSMenuItem *)item;
2159         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2160             DOMCSSStyleDeclaration *style = [self _emptyStyle];
2161             [style setVerticalAlign:@"super"];
2162             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2163         }
2164         return [self _canEditRichly];
2165     } else if (action == @selector(underline:)) {
2166         NSMenuItem *menuItem = (NSMenuItem *)item;
2167         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2168             DOMCSSStyleDeclaration *style = [self _emptyStyle];
2169             [style setProperty:@"-khtml-text-decorations-in-effect" :@"underline" :@""];
2170             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2171         }
2172         return [self _canEditRichly];
2173     } else if (action == @selector(unscript:)) {
2174         NSMenuItem *menuItem = (NSMenuItem *)item;
2175         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2176             DOMCSSStyleDeclaration *style = [self _emptyStyle];
2177             [style setVerticalAlign:@"baseline"];
2178             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2179         }
2180         return [self _canEditRichly];
2181     } else if (action == @selector(_lookUpInDictionaryFromMenu:)) {
2182         return [self _hasSelection];
2183     }
2184     
2185     return YES;
2186 }
2187
2188 - (BOOL)acceptsFirstResponder
2189 {
2190     // Don't accept first responder when we first click on this view.
2191     // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2192     // Do accept first responder at any other time, for example from keyboard events,
2193     // or from calls back from WebCore once we begin mouse-down event handling.
2194     NSEvent *event = [NSApp currentEvent];
2195     if ([event type] == NSLeftMouseDown
2196             && !_private->handlingMouseDownEvent
2197             && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2198         return NO;
2199     }
2200     return YES;
2201 }
2202
2203 - (BOOL)maintainsInactiveSelection
2204 {
2205     // This method helps to determine whether the WebHTMLView should maintain
2206     // an inactive selection when it's not first responder.
2207     // Traditionally, these views have not maintained such selections,
2208     // clearing them when the view was not first responder. However,
2209     // to fix bugs like this one:
2210     // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even 
2211     //                            when they're not firstResponder"
2212     // it was decided to add a switch to act more like an NSTextView.
2213     id nextResponder = [[self window] _newFirstResponderAfterResigning];
2214
2215     // Predict the case where we are losing first responder status only to
2216     // gain it back again.  Want to keep the selection in that case.
2217     if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2218         id contentView = [nextResponder contentView];
2219         if (contentView) {
2220             nextResponder = contentView;
2221         }
2222     }
2223     if ([nextResponder isKindOfClass:[NSClipView class]]) {
2224         id documentView = [nextResponder documentView];
2225         if (documentView) {
2226             nextResponder = documentView;
2227         }
2228     }
2229
2230     if (nextResponder == self)
2231         return YES;
2232
2233     return [[self _webView] maintainsInactiveSelection] || [[self _bridge] isSelectionEditable];
2234 }
2235
2236 - (void)addMouseMovedObserver
2237 {
2238     if (!_private->dataSource || ![self _isTopHTMLView])
2239         return;
2240
2241     // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2242     if (!([[self window] isKeyWindow] || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]))
2243         return;
2244
2245     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2246         name:WKMouseMovedNotification() object:nil];
2247     [self _frameOrBoundsChanged];
2248 }
2249
2250 - (void)removeMouseMovedObserver
2251 {
2252     // Don't remove the observer if we're running the Dashboard.
2253     // FIXME: Right for the windowDidResignKey: case, but wrong for the viewWillMoveToWindow: case.
2254     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2255         return;
2256
2257     [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2258     [[NSNotificationCenter defaultCenter] removeObserver:self
2259         name:WKMouseMovedNotification() object:nil];
2260 }
2261
2262 - (void)addSuperviewObservers
2263 {
2264     // We watch the bounds of our superview, so that we can do a layout when the size
2265     // of the superview changes. This is different from other scrollable things that don't
2266     // need this kind of thing because their layout doesn't change.
2267     
2268     // We need to pay attention to both height and width because our "layout" has to change
2269     // to extend the background the full height of the space and because some elements have
2270     // sizes that are based on the total size of the view.
2271     
2272     NSView *superview = [self superview];
2273     if (superview && [self window]) {
2274         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
2275             name:NSViewFrameDidChangeNotification object:superview];
2276         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
2277             name:NSViewBoundsDidChangeNotification object:superview];
2278
2279         // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2280         // It will check the current size/scroll against the previous layout's size/scroll.  We need to
2281         // do this here to catch the case where the WebView is laid out at one size, removed from its
2282         // window, resized, and inserted into another window.  Our frame/bounds changed notifications
2283         // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2284         [self _frameOrBoundsChanged];
2285     }
2286 }
2287
2288 - (void)removeSuperviewObservers
2289 {
2290     NSView *superview = [self superview];
2291     if (superview && [self window]) {
2292         [[NSNotificationCenter defaultCenter] removeObserver:self
2293             name:NSViewFrameDidChangeNotification object:superview];
2294         [[NSNotificationCenter defaultCenter] removeObserver:self
2295             name:NSViewBoundsDidChangeNotification object:superview];
2296     }
2297 }
2298
2299 - (void)addWindowObservers
2300 {
2301     NSWindow *window = [self window];
2302     if (window) {
2303         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:)
2304             name:NSWindowDidBecomeKeyNotification object:nil];
2305         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:)
2306             name:NSWindowDidResignKeyNotification object:nil];
2307         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:)
2308             name:NSWindowWillCloseNotification object:window];
2309     }
2310 }
2311
2312 - (void)removeWindowObservers
2313 {
2314     NSWindow *window = [self window];
2315     if (window) {
2316         [[NSNotificationCenter defaultCenter] removeObserver:self
2317             name:NSWindowDidBecomeKeyNotification object:nil];
2318         [[NSNotificationCenter defaultCenter] removeObserver:self
2319             name:NSWindowDidResignKeyNotification object:nil];
2320         [[NSNotificationCenter defaultCenter] removeObserver:self
2321             name:NSWindowWillCloseNotification object:window];
2322     }
2323 }
2324
2325 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2326 {
2327     [self removeSuperviewObservers];
2328 }
2329
2330 - (void)viewDidMoveToSuperview
2331 {
2332     // Do this here in case the text size multiplier changed when a non-HTML
2333     // view was installed.
2334     if ([self superview] != nil) {
2335         [self _updateTextSizeMultiplier];
2336         [self addSuperviewObservers];
2337     }
2338 }
2339
2340 - (void)viewWillMoveToWindow:(NSWindow *)window
2341 {
2342     // Don't do anything if we aren't initialized.  This happens
2343     // when decoding a WebView.  When WebViews are decoded their subviews
2344     // are created by initWithCoder: and so won't be normally
2345     // initialized.  The stub views are discarded by WebView.
2346     if (_private) {
2347         // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2348         [self removeMouseMovedObserver];
2349         [self removeWindowObservers];
2350         [self removeSuperviewObservers];
2351         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
2352     
2353         [[self _pluginController] stopAllPlugins];
2354     }
2355 }
2356
2357 - (void)viewDidMoveToWindow
2358 {
2359     // Don't do anything if we aren't initialized.  This happens
2360     // when decoding a WebView.  When WebViews are decoded their subviews
2361     // are created by initWithCoder: and so won't be normally
2362     // initialized.  The stub views are discarded by WebView.
2363     if (_private) {
2364         [self _stopAutoscrollTimer];
2365         if ([self window]) {
2366             _private->lastScrollPosition = [[self superview] bounds].origin;
2367             [self addWindowObservers];
2368             [self addSuperviewObservers];
2369             [self addMouseMovedObserver];
2370
2371             // Schedule this update, rather than making the call right now.
2372             // The reason is that placing the caret in the just-installed view requires
2373             // the HTML/XML document to be available on the WebCore side, but it is not
2374             // at the time this code is running. However, it will be there on the next
2375             // crank of the run loop. Doing this helps to make a blinking caret appear 
2376             // in a new, empty window "automatic".
2377             [self performSelector:@selector(_updateActiveState) withObject:nil afterDelay:0];
2378
2379             [[self _pluginController] startAllPlugins];
2380     
2381             _private->lastScrollPosition = NSZeroPoint;
2382         }
2383     }
2384 }
2385
2386 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2387 {
2388     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2389 }
2390
2391 - (void)viewDidMoveToHostWindow
2392 {
2393     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2394 }
2395
2396
2397 - (void)addSubview:(NSView *)view
2398 {
2399     [super addSubview:view];
2400
2401     if ([WebPluginController isPlugInView:view]) {
2402         [[self _pluginController] addPlugin:view];
2403     }
2404 }
2405
2406 - (void)willRemoveSubview:(NSView *)subview
2407 {
2408     if ([WebPluginController isPlugInView:subview])
2409         [[self _pluginController] destroyPlugin:subview];
2410     [super willRemoveSubview:subview];
2411 }
2412
2413 - (void)reapplyStyles
2414 {
2415     if (!_private->needsToApplyStyles) {
2416         return;
2417     }
2418     
2419 #ifdef _KWQ_TIMING        
2420     double start = CFAbsoluteTimeGetCurrent();
2421 #endif
2422
2423     [[self _bridge] reapplyStylesForDeviceType:
2424         _private->printing ? WebCoreDevicePrinter : WebCoreDeviceScreen];
2425     
2426 #ifdef _KWQ_TIMING        
2427     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2428     LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2429 #endif
2430
2431     _private->needsToApplyStyles = NO;
2432 }
2433
2434 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2435 // minPageWidth==0 implies a non-printing layout
2436 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
2437 {
2438     [self reapplyStyles];
2439     
2440     // Ensure that we will receive mouse move events.  Is this the best place to put this?
2441     [[self window] setAcceptsMouseMovedEvents: YES];
2442     WKSetNSWindowShouldPostEventNotifications([self window], YES);
2443
2444     if (!_private->needsLayout) {
2445         return;
2446     }
2447
2448 #ifdef _KWQ_TIMING        
2449     double start = CFAbsoluteTimeGetCurrent();
2450 #endif
2451
2452     LOG(View, "%@ doing layout", self);
2453
2454     if (minPageWidth > 0.0) {
2455         [[self _bridge] forceLayoutWithMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
2456     } else {
2457         [[self _bridge] forceLayoutAdjustingViewSize:adjustViewSize];
2458     }
2459     _private->needsLayout = NO;
2460     
2461     if (!_private->printing) {
2462         // get size of the containing dynamic scrollview, so
2463         // appearance and disappearance of scrollbars will not show up
2464         // as a size change
2465         NSSize newLayoutFrameSize = [[[self superview] superview] frame].size;
2466         if (_private->laidOutAtLeastOnce && !NSEqualSizes(_private->lastLayoutFrameSize, newLayoutFrameSize)) {
2467             [[self _bridge] sendResizeEvent];
2468             if ([[self _bridge] needsLayout])
2469                 [[self _bridge] forceLayoutAdjustingViewSize:NO];
2470         }
2471         _private->laidOutAtLeastOnce = YES;
2472         _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
2473         _private->lastLayoutFrameSize = newLayoutFrameSize;
2474     }
2475
2476 #ifdef _KWQ_TIMING        
2477     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2478     LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
2479 #endif
2480 }
2481
2482 - (void)layout
2483 {
2484     [self layoutToMinimumPageWidth:0.0 maximumPageWidth:0.0 adjustingViewSize:NO];
2485 }
2486
2487 - (NSMenu *)menuForEvent:(NSEvent *)event
2488 {
2489     [_private->compController endRevertingChange:NO moveLeft:NO];
2490
2491     _private->handlingMouseDownEvent = YES;
2492     BOOL handledEvent = [[self _bridge] sendContextMenuEvent:event];
2493     _private->handlingMouseDownEvent = NO;
2494     if (handledEvent)
2495         return nil;
2496
2497     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2498     NSDictionary *element = [self elementAtPoint:point];
2499     return [[self _webView] _menuForElement:element defaultItems:nil];
2500 }
2501
2502 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
2503 {
2504     if (![string length])
2505         return NO;
2506
2507     return [[self _bridge] searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag];
2508 }
2509
2510 - (void)deselectText
2511 {
2512     [[self _bridge] deselectText];
2513 }
2514
2515 - (BOOL)isOpaque
2516 {
2517     return [[self _webView] drawsBackground];
2518 }
2519
2520 - (void)setNeedsDisplay:(BOOL)flag
2521 {
2522     LOG(View, "%@ flag = %d", self, (int)flag);
2523     [super setNeedsDisplay: flag];
2524 }
2525
2526 - (void)setNeedsLayout: (BOOL)flag
2527 {
2528     LOG(View, "%@ flag = %d", self, (int)flag);
2529     _private->needsLayout = flag;
2530 }
2531
2532
2533 - (void)setNeedsToApplyStyles: (BOOL)flag
2534 {
2535     LOG(View, "%@ flag = %d", self, (int)flag);
2536     _private->needsToApplyStyles = flag;
2537 }
2538
2539 - (void)drawSingleRect:(NSRect)rect
2540 {
2541     [NSGraphicsContext saveGraphicsState];
2542     NSRectClip(rect);
2543         
2544     ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
2545
2546     [(WebClipView *)[self superview] setAdditionalClip:rect];
2547
2548     NS_DURING {
2549         if ([self _transparentBackground]) {
2550             [[NSColor clearColor] set];
2551             NSRectFill (rect);
2552         }
2553         
2554         [[self _bridge] drawRect:rect];        
2555         WebView *webView = [self _webView];
2556         [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
2557         [(WebClipView *)[self superview] resetAdditionalClip];
2558
2559         [NSGraphicsContext restoreGraphicsState];
2560     } NS_HANDLER {
2561         [(WebClipView *)[self superview] resetAdditionalClip];
2562         [NSGraphicsContext restoreGraphicsState];
2563         LOG_ERROR("Exception caught while drawing: %@", localException);
2564         [localException raise];
2565     } NS_ENDHANDLER
2566 }
2567
2568 - (void)drawRect:(NSRect)rect
2569 {
2570     LOG(View, "%@ drawing", self);
2571
2572     const NSRect *rects;
2573     WebNSInteger count;
2574     [self getRectsBeingDrawn:&rects count:&count];
2575
2576     BOOL subviewsWereSetAside = _private->subviewsSetAside;
2577     if (subviewsWereSetAside)
2578         [self _restoreSubviews];
2579
2580 #ifdef _KWQ_TIMING
2581     double start = CFAbsoluteTimeGetCurrent();
2582 #endif
2583
2584     // If count == 0 here, use the rect passed in for drawing. This is a workaround for:
2585     // <rdar://problem/3908282> REGRESSION (Mail): No drag image dragging selected text in Blot and Mail
2586     // The reason for the workaround is that this method is called explicitly from the code
2587     // to generate a drag image, and at that time, getRectsBeingDrawn:count: will return a zero count.
2588     const int cRectThreshold = 10;
2589     const float cWastedSpaceThreshold = 0.75f;
2590     BOOL useUnionedRect = (count <= 1) || (count > cRectThreshold);
2591     if (!useUnionedRect) {
2592         // Attempt to guess whether or not we should use the unioned rect or the individual rects.
2593         // We do this by computing the percentage of "wasted space" in the union.  If that wasted space
2594         // is too large, then we will do individual rect painting instead.
2595         float unionPixels = (rect.size.width * rect.size.height);
2596         float singlePixels = 0;
2597         for (int i = 0; i < count; ++i)
2598             singlePixels += rects[i].size.width * rects[i].size.height;
2599         float wastedSpace = 1 - (singlePixels / unionPixels);
2600         if (wastedSpace <= cWastedSpaceThreshold)
2601             useUnionedRect = YES;
2602     }
2603     
2604     if (useUnionedRect)
2605         [self drawSingleRect:rect];
2606     else
2607         for (int i = 0; i < count; ++i)
2608             [self drawSingleRect:rects[i]];
2609
2610 #ifdef _KWQ_TIMING
2611     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2612     LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
2613 #endif
2614
2615     if (subviewsWereSetAside)
2616         [self _setAsideSubviews];
2617 }
2618
2619 // Turn off the additional clip while computing our visibleRect.
2620 - (NSRect)visibleRect
2621 {
2622     if (!([[self superview] isKindOfClass:[WebClipView class]]))
2623         return [super visibleRect];
2624         
2625     WebClipView *clipView = (WebClipView *)[self superview];
2626
2627     BOOL hasAdditionalClip = [clipView hasAdditionalClip];
2628     if (!hasAdditionalClip) {
2629         return [super visibleRect];
2630     }
2631     
2632     NSRect additionalClip = [clipView additionalClip];
2633     [clipView resetAdditionalClip];
2634     NSRect visibleRect = [super visibleRect];
2635     [clipView setAdditionalClip:additionalClip];
2636     return visibleRect;
2637 }
2638
2639 - (BOOL)isFlipped 
2640 {
2641     return YES;
2642 }
2643
2644 - (void)windowDidBecomeKey:(NSNotification *)notification
2645 {
2646     NSWindow *keyWindow = [notification object];
2647
2648     if (keyWindow == [self window])
2649         [self addMouseMovedObserver];
2650
2651     if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
2652         [self _updateActiveState];
2653 }
2654
2655 - (void)windowDidResignKey:(NSNotification *)notification
2656 {
2657     NSWindow *formerKeyWindow = [notification object];
2658
2659     if (formerKeyWindow == [self window])
2660         [self removeMouseMovedObserver];
2661
2662     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
2663         [self _updateActiveState];
2664         [_private->compController endRevertingChange:NO moveLeft:NO];
2665     }
2666 }
2667
2668 - (void)windowWillClose:(NSNotification *)notification
2669 {
2670     [_private->compController endRevertingChange:NO moveLeft:NO];
2671     [[self _pluginController] destroyAllPlugins];
2672 }
2673
2674 - (void)scrollWheel:(NSEvent *)event
2675 {
2676     [self retain];
2677     
2678     if (![[self _bridge] sendScrollWheelEvent:event]) {
2679         [[self nextResponder] scrollWheel:event];
2680     }    
2681     
2682     [self release];
2683 }
2684
2685 - (BOOL)_isSelectionEvent:(NSEvent *)event
2686 {
2687     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2688     return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
2689 }
2690
2691 - (void)_setMouseDownEvent:(NSEvent *)event
2692 {
2693     ASSERT([event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
2694
2695     if (event == _private->mouseDownEvent) {
2696         return;
2697     }
2698
2699     [event retain];
2700     [_private->mouseDownEvent release];
2701     _private->mouseDownEvent = event;
2702
2703     [_private->firstResponderTextViewAtMouseDownTime release];
2704     
2705     // The only code that checks this ivar only cares about NSTextViews. The code used to be more general,
2706     // but it caused reference cycles leading to world leaks (see 4557386). We should be able to eliminate
2707     // firstResponderTextViewAtMouseDownTime entirely when all the form controls are native widgets, because 
2708     // the only caller (in WebCore) will be unnecessary.
2709     NSResponder *firstResponder = [[self window] firstResponder];
2710     if ([firstResponder isKindOfClass:[NSTextView class]])
2711         _private->firstResponderTextViewAtMouseDownTime = [firstResponder retain];
2712     else
2713         _private->firstResponderTextViewAtMouseDownTime = nil;
2714 }
2715
2716 - (BOOL)acceptsFirstMouse:(NSEvent *)event
2717 {
2718     NSView *hitView = [self _hitViewForEvent:event];
2719     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2720     [hitHTMLView _setMouseDownEvent:event];
2721     
2722     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
2723         return YES;
2724     
2725     if (hitHTMLView != nil) {
2726         [[hitHTMLView _bridge] setActivationEventNumber:[event eventNumber]];
2727         return [hitHTMLView _isSelectionEvent:event] ? [[hitHTMLView _bridge] eventMayStartDrag:event] : NO;
2728     } else {
2729         return [hitView acceptsFirstMouse:event];
2730     }
2731 }
2732
2733 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
2734 {
2735     NSView *hitView = [self _hitViewForEvent:event];
2736     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2737     if (hitHTMLView != nil) {
2738         [hitHTMLView _setMouseDownEvent:event];
2739         return [hitHTMLView _isSelectionEvent:event] ? [[hitHTMLView _bridge] eventMayStartDrag:event] : NO;
2740     } else {
2741         return [hitView shouldDelayWindowOrderingForEvent:event];
2742     }
2743 }
2744
2745 - (void)mouseDown:(NSEvent *)event
2746 {
2747     [self retain];
2748
2749     _private->handlingMouseDownEvent = YES;
2750
2751     // Record the mouse down position so we can determine drag hysteresis.
2752     [self _setMouseDownEvent:event];
2753
2754     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2755     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2756         goto done;
2757
2758     [_private->compController endRevertingChange:NO moveLeft:NO];
2759
2760     // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
2761     // We don't want to pass them along to KHTML a second time.
2762     if (!([event modifierFlags] & NSControlKeyMask)) {
2763         _private->ignoringMouseDraggedEvents = NO;
2764
2765         // Don't do any mouseover while the mouse is down.
2766         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
2767
2768         // Let KHTML get a chance to deal with the event. This will call back to us
2769         // to start the autoscroll timer if appropriate.
2770         [[self _bridge] mouseDown:event];
2771     }
2772
2773 done:
2774     [_private->firstResponderTextViewAtMouseDownTime release];
2775     _private->firstResponderTextViewAtMouseDownTime = nil;
2776
2777     _private->handlingMouseDownEvent = NO;
2778     
2779     [self release];
2780 }
2781
2782 - (void)dragImage:(NSImage *)dragImage
2783                at:(NSPoint)at
2784            offset:(NSSize)offset
2785             event:(NSEvent *)event
2786        pasteboard:(NSPasteboard *)pasteboard
2787            source:(id)source
2788         slideBack:(BOOL)slideBack
2789 {
2790     [self _stopAutoscrollTimer];
2791
2792     WebHTMLView *topHTMLView = [self _topHTMLView];
2793     if (self != topHTMLView) {
2794         [topHTMLView dragImage:dragImage at:[self convertPoint:at toView:topHTMLView]
2795             offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
2796         return;
2797     }
2798
2799     WebView *webView = [self _webView];
2800
2801     _private->initiatedDrag = YES;
2802     [webView _setInitiatedDrag:YES];
2803
2804     // Retain this view during the drag because it may be released before the drag ends.
2805     [self retain];
2806
2807     id UIDelegate = [webView UIDelegate];
2808     // If a delegate takes over the drag but never calls draggedImage: endedAt:, we'll leak the WebHTMLView.
2809     if ([UIDelegate respondsToSelector:@selector(webView:dragImage:at:offset:event:pasteboard:source:slideBack:forView:)])
2810         [UIDelegate webView:webView dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack forView:self];
2811     else
2812         [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
2813 }
2814
2815 - (void)mouseDragged:(NSEvent *)event
2816 {
2817     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2818     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2819         return;
2820
2821     [self retain];
2822
2823     if (!_private->ignoringMouseDraggedEvents)
2824         [[self _bridge] mouseDragged:event];
2825
2826     [self release];
2827 }
2828
2829 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
2830 {
2831     ASSERT([self _isTopHTMLView]);
2832
2833     if (_private->webCoreDragOp == NSDragOperationNone)
2834         return NSDragOperationGeneric | NSDragOperationCopy;
2835     return _private->webCoreDragOp;
2836 }
2837
2838 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
2839 {
2840     ASSERT([self _isTopHTMLView]);
2841
2842     NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
2843     NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + _private->dragOffset.x, windowImageLoc.y + _private->dragOffset.y);
2844     [[self _bridge] dragSourceMovedTo:windowMouseLoc];
2845 }
2846
2847 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
2848 {
2849     ASSERT(![self _webView] || [self _isTopHTMLView]);
2850
2851     NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
2852     NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + _private->dragOffset.x, windowImageLoc.y + _private->dragOffset.y);
2853     [[self _bridge] dragSourceEndedAt:windowMouseLoc operation:operation];
2854
2855     _private->initiatedDrag = NO;
2856     [[self _webView] _setInitiatedDrag:NO];
2857     
2858     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
2859     _private->ignoringMouseDraggedEvents = YES;
2860     
2861     // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
2862     // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
2863     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2864                                             location:windowMouseLoc
2865                                        modifierFlags:[[NSApp currentEvent] modifierFlags]
2866                                            timestamp:[NSDate timeIntervalSinceReferenceDate]
2867                                         windowNumber:[[self window] windowNumber]
2868                                              context:[[NSApp currentEvent] context]
2869                                          eventNumber:0 clickCount:0 pressure:0];
2870     [self mouseUp:fakeEvent]; // This will also update the mouseover state.
2871     
2872     // Balance the previous retain from when the drag started.
2873     [self release];
2874 }
2875
2876 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
2877 {
2878     ASSERT([self _isTopHTMLView]);
2879     ASSERT(_private->draggingImageURL);
2880
2881     NSFileWrapper *wrapper = [[self _dataSource] _fileWrapperForURL:_private->draggingImageURL];
2882     if (wrapper == nil) {
2883         LOG_ERROR("Failed to create image file. Did the source image change while dragging? (<rdar://problem/4244861>)");
2884         return nil;
2885     }
2886
2887     // FIXME: Report an error if we fail to create a file.
2888     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
2889     path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
2890     if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
2891         LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
2892
2893     return [NSArray arrayWithObject:[path lastPathComponent]];
2894 }
2895
2896 - (void)mouseUp:(NSEvent *)event
2897 {
2898     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2899     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2900         return;
2901
2902     [self retain];
2903
2904     [self _stopAutoscrollTimer];
2905     [[self _bridge] mouseUp:event];
2906     [self _updateMouseoverWithFakeEvent];
2907
2908     [self release];
2909 }
2910
2911 - (void)mouseMovedNotification:(NSNotification *)notification
2912 {
2913     [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
2914 }
2915
2916 // returning YES from this method is the way we tell AppKit that it is ok for this view
2917 // to be in the key loop even when "tab to all controls" is not on.
2918 - (BOOL)needsPanelToBecomeKey
2919 {
2920     return YES;
2921 }
2922
2923 - (NSView *)nextValidKeyView
2924 {
2925     NSView *view = nil;
2926     BOOL lookInsideWebFrameViews = YES;
2927     if ([self isHiddenOrHasHiddenAncestor]) {
2928         lookInsideWebFrameViews = NO;
2929     } else if ([self _frame] == [[self _webView] mainFrame]) {
2930         // Check for case where first responder is last frame in a frameset, and we are
2931         // the top-level documentView.
2932         NSResponder *firstResponder = [[self window] firstResponder];
2933         if ((firstResponder != self) && [firstResponder isKindOfClass:[WebHTMLView class]] && ([(NSView *)firstResponder nextKeyView] == nil)) {
2934             lookInsideWebFrameViews = NO;
2935         }
2936     }
2937     
2938     if (lookInsideWebFrameViews) {
2939         view = [[self _bridge] nextKeyViewInsideWebFrameViews];
2940     }
2941     
2942     if (view == nil) {
2943         view = [super nextValidKeyView];
2944         // If there's no next view wired up, we must be in the last subframe, or we are
2945         // being called at an unusual time when the views have not yet been wired together.
2946         // There's no direct link to the next valid key view; get it from the bridge.
2947         // Note that view == self here when nextKeyView returns nil, due to AppKit oddness.
2948         // We'll check for both nil and self in case the AppKit oddness goes away.
2949         // WebFrameView has this same kind of logic for the previousValidKeyView case.
2950         if (view == nil || view == self) {
2951             view = [[self _bridge] nextValidKeyViewOutsideWebFrameViews];
2952         }
2953     }
2954         
2955     return view;
2956 }
2957
2958 - (NSView *)previousValidKeyView
2959 {
2960     NSView *view = nil;
2961     if (![self isHiddenOrHasHiddenAncestor])
2962         view = [[self _bridge] previousKeyViewInsideWebFrameViews];
2963     if (view == nil)
2964         view = [super previousValidKeyView];
2965     return view;
2966 }
2967
2968 - (BOOL)becomeFirstResponder
2969 {
2970     NSView *view = nil;
2971     if (![[self _webView] _isPerformingProgrammaticFocus] && !_private->willBecomeFirstResponderForNodeFocus) {
2972         switch ([[self window] keyViewSelectionDirection]) {
2973             case NSDirectSelection:
2974                 break;
2975             case NSSelectingNext:
2976                 view = [[self _bridge] nextKeyViewInsideWebFrameViews];
2977                 break;
2978             case NSSelectingPrevious:
2979                 view = [[self _bridge] previousKeyViewInsideWebFrameViews];
2980                 break;
2981         }
2982     }
2983     _private->willBecomeFirstResponderForNodeFocus = NO;
2984     if (view)
2985         [[self window] makeFirstResponder:view];
2986     [self _updateActiveState];
2987     [self _updateFontPanel];
2988     _private->startNewKillRingSequence = YES;
2989     return YES;
2990 }
2991
2992 - (BOOL)resignFirstResponder
2993 {
2994     BOOL resign = [super resignFirstResponder];
2995     if (resign) {
2996         [_private->compController endRevertingChange:NO moveLeft:NO];
2997         _private->resigningFirstResponder = YES;
2998         if (![self maintainsInactiveSelection]) { 
2999             if ([[self _webView] _isPerformingProgrammaticFocus])
3000                 [self deselectText];
3001             else
3002                 [self deselectAll];
3003         }
3004         [self _updateActiveState];
3005         _private->resigningFirstResponder = NO;
3006         _private->willBecomeFirstResponderForNodeFocus = NO;
3007     }
3008     return resign;
3009 }
3010
3011 - (void)setDataSource:(WebDataSource *)dataSource 
3012 {
3013     ASSERT(!_private->dataSource);
3014     _private->dataSource = [dataSource retain];
3015     [_private->pluginController setDataSource:dataSource];
3016     [self addMouseMovedObserver];
3017 }
3018
3019 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3020 {
3021 }
3022
3023 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3024 // key.  WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3025 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3026 - (void)updateCell:(NSCell*)cell
3027 {
3028 }
3029
3030 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3031 // pageWidth != 0 implies we will relayout to a new width
3032 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
3033 {
3034     WebFrame *frame = [self _frame];
3035     NSArray *subframes = [frame childFrames];
3036     unsigned n = [subframes count];
3037     unsigned i;
3038     for (i = 0; i != n; ++i) {
3039         WebFrame *subframe = [subframes objectAtIndex:i];
3040         WebFrameView *frameView = [subframe frameView];
3041         if ([[subframe dataSource] _isDocumentHTML]) {
3042             [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:adjustViewSize];
3043         }
3044     }
3045
3046     if (printing != _private->printing) {
3047         [_private->pageRects release];
3048         _private->pageRects = nil;
3049         _private->printing = printing;
3050         [self setNeedsToApplyStyles:YES];
3051         [self setNeedsLayout:YES];
3052         [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
3053         if (!printing) {
3054             // Can't do this when starting printing or nested printing won't work, see 3491427.
3055             [self setNeedsDisplay:NO];
3056         }
3057     }
3058 }
3059
3060 - (BOOL)canPrintHeadersAndFooters
3061 {
3062     return YES;
3063 }
3064
3065 // This is needed for the case where the webview is embedded in the view that's being printed.
3066 // It shouldn't be called when the webview is being printed directly.
3067 - (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit
3068 {
3069     // This helps when we print as part of a larger print process.
3070     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3071     BOOL wasInPrintingMode = _private->printing;
3072     if (!wasInPrintingMode)
3073         [self _setPrinting:YES minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
3074
3075     [[self _bridge] adjustPageHeightNew:newBottom top:oldTop bottom:oldBottom limit:bottomLimit];
3076     
3077     if (!wasInPrintingMode)
3078         [self _setPrinting:NO minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
3079 }
3080
3081 - (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
3082 {
3083     NSPrintInfo *printInfo = [printOperation printInfo];
3084     return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
3085 }
3086
3087 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3088 {
3089     float viewWidth = NSWidth([self bounds]);
3090     if (viewWidth < 1) {
3091         LOG_ERROR("%@ has no width when printing", self);
3092         return 1.0;
3093     }
3094
3095     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3096     float maxShrinkToFitScaleFactor = 1/PrintingMaximumShrinkFactor;
3097     float shrinkToFitScaleFactor = [self _availablePaperWidthForPrintOperation:printOperation]/viewWidth;
3098     return userScaleFactor * MAX(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor);
3099 }
3100
3101 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3102 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3103 // if AppKit makes it SPI/API.
3104 - (float)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation 
3105 {
3106     return [self _scaleFactorForPrintOperation:printOperation];
3107 }
3108
3109 // This is used for Carbon printing. At some point we might want to make this public API.
3110 - (void)setPageWidthForPrinting:(float)pageWidth
3111 {
3112     [self _setPrinting:NO minimumPageWidth:0. maximumPageWidth:0. adjustViewSize:NO];
3113     [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
3114 }
3115
3116 - (void)_endPrintMode
3117 {
3118     [self _setPrinting:NO minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:YES];
3119     [[self window] setAutodisplay:YES];
3120 }
3121
3122 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3123 {
3124     ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3125     NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3126     if (initiatingOperation == currentOperation) {
3127         // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3128         // being extra paranoid here since the printing code is so fragile. Delay the cleanup
3129         // further.
3130         ASSERT_NOT_REACHED();
3131         [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3132     } else if ([currentOperation view] == self) {
3133         // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3134         // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3135         // fragile. Do nothing, because we don't want to break the print job currently in progress, and
3136         // the print job currently in progress is responsible for its own cleanup.
3137         ASSERT_NOT_REACHED();
3138     } else {
3139         // The print job that kicked off this delayed call has finished, and this view is not being
3140         // printed again. We expect that no other print job has started. Since this delayed call wasn't
3141         // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3142         // the print mode here.
3143         ASSERT(currentOperation == nil);
3144         [self _endPrintMode];
3145     }
3146 }
3147
3148 // Return the number of pages available for printing
3149 - (BOOL)knowsPageRange:(NSRangePointer)range
3150 {
3151     // Must do this explicit display here, because otherwise the view might redisplay while the print
3152     // sheet was up, using printer fonts (and looking different).
3153     [self displayIfNeeded];
3154     [[self window] setAutodisplay:NO];
3155     
3156     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
3157     // according to the paper size
3158     float minLayoutWidth = 0.0;
3159     float maxLayoutWidth = 0.0;
3160     if (![[self _bridge] isFrameSet]) {
3161         float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
3162         minLayoutWidth = paperWidth*PrintingMinimumShrinkFactor;
3163         maxLayoutWidth = paperWidth*PrintingMaximumShrinkFactor;
3164     }
3165     [self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; // will relayout
3166     NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3167     // Certain types of errors, including invalid page ranges, can cause beginDocument and
3168     // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3169     // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3170     // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3171     // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3172     // which is after beginDocument and endDocument would be called.
3173     [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3174     [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3175     
3176     // There is a theoretical chance that someone could do some drawing between here and endDocument,
3177     // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3178     // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3179
3180     range->location = 1;
3181     float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3182     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3183     [_private->pageRects release];
3184     NSArray *newPageRects = [[self _bridge] computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3185                                                                           printHeight:floorf([self _calculatePrintHeight]/totalScaleFactor)];
3186     // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3187     // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3188     // a blank page (with correct-looking header and footer if that option is on), which matches
3189     // the behavior of IE and Camino at least.
3190     if ([newPageRects count] == 0)
3191         newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3192     _private->pageRects = [newPageRects retain];
3193     
3194     range->length = [_private->pageRects count];
3195     
3196     return YES;
3197 }
3198
3199 // Return the drawing rectangle for a particular page number
3200 - (NSRect)rectForPage:(int)page
3201 {
3202     return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3203 }
3204
3205 - (void)drawPageBorderWithSize:(NSSize)borderSize
3206 {
3207     ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));    
3208     [[self _webView] _drawHeaderAndFooter];
3209 }
3210
3211 - (void)beginDocument
3212 {
3213     NS_DURING
3214         // From now on we'll get a chance to call _endPrintMode in either beginDocument or
3215         // endDocument, so we can cancel the "just in case" pending call.
3216         [NSObject cancelPreviousPerformRequestsWithTarget:self
3217                                                  selector:@selector(_delayedEndPrintMode:)
3218                                                    object:[NSPrintOperation currentOperation]];
3219         [super beginDocument];
3220     NS_HANDLER
3221         // Exception during [super beginDocument] means that endDocument will not get called,
3222         // so we need to clean up our "print mode" here.
3223         [self _endPrintMode];
3224     NS_ENDHANDLER
3225 }
3226
3227 - (void)endDocument
3228 {
3229     [super endDocument];
3230     // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO 
3231     [self _endPrintMode];
3232 }
3233
3234 - (BOOL)_interceptEditingKeyEvent:(NSEvent *)event
3235 {   
3236     // Use WebView's tabKeyCyclesThroughElements state to determine whether or not
3237     // to process tab key events. The idea here is that tabKeyCyclesThroughElements
3238     // will be YES when this WebView is being used in a browser, and we desire the
3239     // behavior where tab moves to the next element in tab order. If tabKeyCyclesThroughElements
3240     // is NO, it is likely that the WebView is being embedded as the whole view, as in Mail,
3241     // and tabs should input tabs as expected in a text editor. Using Option-Tab always cycles
3242     // through elements.
3243
3244     if ([[self _webView] tabKeyCyclesThroughElements] && [event _web_isTabKeyEvent]) 
3245         return NO;
3246
3247     if (![[self _webView] tabKeyCyclesThroughElements] && [event _web_isOptionTabKeyEvent])
3248         return NO;
3249
3250     // Now process the key normally
3251     [self interpretKeyEvents:[NSArray arrayWithObject:event]];
3252     return YES;
3253 }
3254
3255 - (void)keyDown:(NSEvent *)event
3256 {
3257     [self retain];
3258
3259     BOOL callSuper = NO;
3260
3261     _private->keyDownEvent = event;
3262
3263     WebFrameBridge *bridge = [self _bridge];
3264     if ([bridge interceptKeyEvent:event toView:self]) {
3265         // WebCore processed a key event, bail on any outstanding complete: UI
3266         [_private->compController endRevertingChange:YES moveLeft:NO];
3267     } else if (_private->compController && [_private->compController filterKeyDown:event]) {
3268         // Consumed by complete: popup window
3269     } else {
3270         // We're going to process a key event, bail on any outstanding complete: UI
3271         [_private->compController endRevertingChange:YES moveLeft:NO];
3272         BOOL handledKey = [self _canEdit] && [self _interceptEditingKeyEvent:event];
3273         if (!handledKey)
3274             callSuper = YES;
3275     }
3276     if (callSuper)
3277         [super keyDown:event];
3278     else
3279         [NSCursor setHiddenUntilMouseMoves:YES];
3280
3281     _private->keyDownEvent = nil;
3282     
3283     [self release];
3284 }
3285
3286 - (void)keyUp:(NSEvent *)event
3287 {
3288     [self retain];
3289     if (![[self _bridge] interceptKeyEvent:event toView:self])
3290         [super keyUp:event];    
3291     [self release];
3292 }
3293
3294 - (id)accessibilityAttributeValue:(NSString*)attributeName
3295 {
3296     if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
3297         id accTree = [[self _bridge] accessibilityTree];
3298         if (accTree)
3299             return [NSArray arrayWithObject:accTree];
3300         return nil;
3301     }
3302     return [super accessibilityAttributeValue:attributeName];
3303 }
3304
3305 - (id)accessibilityFocusedUIElement
3306 {
3307     id accTree = [[self _bridge] accessibilityTree];
3308     if (accTree)
3309         return [accTree accessibilityFocusedUIElement];
3310     return self;
3311 }
3312
3313 - (id)accessibilityHitTest:(NSPoint)point
3314 {
3315     id accTree = [[self _bridge] accessibilityTree];
3316     if (accTree) {
3317         NSPoint windowCoord = [[self window] convertScreenToBase:point];
3318         return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
3319     }
3320     return self;
3321 }
3322
3323 - (id)_accessibilityParentForSubview:(NSView *)subview
3324 {
3325     id accTree = [[self _bridge] accessibilityTree];
3326     if (!accTree)
3327         return self;
3328     id parent = [accTree _accessibilityParentForSubview:subview];
3329     if (!parent)
3330         return self;
3331     return parent;
3332 }
3333
3334 - (void)centerSelectionInVisibleArea:(id)sender
3335 {
3336     [[self _bridge] centerSelectionInVisibleArea];
3337 }
3338
3339 - (void)_alterCurrentSelection:(WebSelectionAlteration)alteration direction:(WebBridgeSelectionDirection)direction granularity:(WebBridgeSelectionGranularity)granularity
3340 {
3341     if (![self _canAlterCurrentSelection])
3342         return;
3343         
3344     WebFrameBridge *bridge = [self _bridge];
3345     DOMRange *proposedRange = [bridge rangeByAlteringCurrentSelection:alteration direction:direction granularity:granularity];
3346     WebView *webView = [self _webView];
3347     if ([[webView _editingDelegateForwarder] webView:webView shouldChangeSelectedDOMRange:[self _selectedRange] toDOMRange:proposedRange affinity:[bridge selectionAffinity] stillSelecting:NO]) {
3348         [bridge alterCurrentSelection:alteration direction:direction granularity:granularity];
3349     }
3350 }
3351
3352 - (void)_alterCurrentSelection:(WebSelectionAlteration)alteration verticalDistance:(float)verticalDistance
3353 {
3354     if (![self _canAlterCurrentSelection])
3355         return;
3356         
3357     WebFrameBridge *bridge = [self _bridge];
3358     DOMRange *proposedRange = [bridge rangeByAlteringCurrentSelection:alteration verticalDistance:verticalDistance];
3359     WebView *webView = [self _webView];
3360     if ([[webView _editingDelegateForwarder] webView:webView shouldChangeSelectedDOMRange:[self _selectedRange] toDOMRange:proposedRange affinity:[bridge selectionAffinity] stillSelecting:NO]) {
3361         [bridge alterCurrentSelection:alteration verticalDistance:verticalDistance];
3362     }
3363 }
3364
3365 - (void)moveBackward:(id)sender
3366 {
3367     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectByCharacter];
3368 }
3369
3370 - (void)moveBackwardAndModifySelection:(id)sender
3371 {
3372     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectByCharacter];
3373 }
3374
3375 - (void)moveDown:(id)sender
3376 {
3377     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectByLine];
3378 }
3379
3380 - (void)moveDownAndModifySelection:(id)sender
3381 {
3382     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectByLine];
3383 }
3384
3385 - (void)moveForward:(id)sender
3386 {
3387     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectByCharacter];
3388 }
3389
3390 - (void)moveForwardAndModifySelection:(id)sender
3391 {
3392     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectByCharacter];
3393 }
3394
3395 - (void)moveLeft:(id)sender
3396 {
3397     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectLeft granularity:WebBridgeSelectByCharacter];
3398 }
3399
3400 - (void)moveLeftAndModifySelection:(id)sender
3401 {
3402     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectLeft granularity:WebBridgeSelectByCharacter];
3403 }
3404
3405 - (void)moveRight:(id)sender
3406 {
3407     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectRight granularity:WebBridgeSelectByCharacter];
3408 }
3409
3410 - (void)moveRightAndModifySelection:(id)sender
3411 {
3412     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectRight granularity:WebBridgeSelectByCharacter];
3413 }
3414
3415 - (void)moveToBeginningOfDocument:(id)sender
3416 {
3417     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToDocumentBoundary];
3418 }
3419
3420 - (void)moveToBeginningOfDocumentAndModifySelection:(id)sender
3421 {
3422     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToDocumentBoundary];
3423 }
3424
3425 - (void)moveToBeginningOfSentence:(id)sender
3426 {
3427     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToSentenceBoundary];
3428 }
3429
3430 - (void)moveToBeginningOfSentenceAndModifySelection:(id)sender
3431 {
3432     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToSentenceBoundary];
3433 }
3434
3435 - (void)moveToBeginningOfLine:(id)sender
3436 {
3437     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToLineBoundary];
3438 }
3439
3440 - (void)moveToBeginningOfLineAndModifySelection:(id)sender
3441 {
3442     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToLineBoundary];
3443 }
3444
3445 - (void)moveToBeginningOfParagraph:(id)sender
3446 {
3447     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToParagraphBoundary];
3448 }
3449
3450 - (void)moveToBeginningOfParagraphAndModifySelection:(id)sender
3451 {
3452     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToParagraphBoundary];
3453 }
3454
3455 - (void)moveToEndOfDocument:(id)sender
3456 {
3457     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectToDocumentBoundary];
3458 }
3459
3460 - (void)moveToEndOfDocumentAndModifySelection:(id)sender
3461 {
3462     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectToDocumentBoundary];
3463 }
3464
3465 - (void)moveToEndOfSentence:(id)sender
3466 {
3467     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectToSentenceBoundary];
3468 }
3469
3470 - (void)moveToEndOfSentenceAndModifySelection:(id)sender
3471 {
3472     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectToSentenceBoundary];
3473 }
3474
3475 - (void)moveToEndOfLine:(id)sender
3476 {
3477     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectToLineBoundary];
3478 }
3479
3480 - (void)moveToEndOfLineAndModifySelection:(id)sender
3481 {
3482     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectToLineBoundary];
3483 }
3484
3485 - (void)moveToEndOfParagraph:(id)sender
3486 {
3487     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectToParagraphBoundary];
3488 }
3489
3490 - (void)moveToEndOfParagraphAndModifySelection:(id)sender
3491 {
3492     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectToParagraphBoundary];
3493 }
3494
3495 - (void)moveParagraphBackwardAndModifySelection:(id)sender
3496 {
3497     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectByParagraph];
3498 }
3499
3500 - (void)moveParagraphForwardAndModifySelection:(id)sender
3501 {
3502     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectByParagraph];
3503 }
3504
3505 - (void)moveUp:(id)sender
3506 {
3507     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectByLine];
3508 }
3509
3510 - (void)moveUpAndModifySelection:(id)sender
3511 {
3512     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectByLine];
3513 }
3514
3515 - (void)moveWordBackward:(id)sender
3516 {
3517     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectByWord];
3518 }
3519
3520 - (void)moveWordBackwardAndModifySelection:(id)sender
3521 {
3522     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectByWord];
3523 }
3524
3525 - (void)moveWordForward:(id)sender