fc23f8ab06c6940887cd6900ed34b3a2470088da
[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     displayRect = NSIntersectionRect(displayRect, [self bounds]);
1492
1493     if (!testDirtyRect || [self needsDisplay]) {
1494         if (testDirtyRect) {
1495             NSRect dirtyRect = [self _dirtyRect];
1496             displayRect = NSIntersectionRect(displayRect, dirtyRect);
1497         }
1498         if (!NSIsEmptyRect(displayRect)) {
1499             [self _layoutIfNeeded];
1500         }
1501     }
1502
1503     [super _web_layoutIfNeededRecursive:displayRect testDirtyRect:NO];
1504 }
1505
1506 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1507 {
1508     if (_private->autoscrollTimer == nil) {
1509         _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1510             target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1511         _private->autoscrollTriggerEvent = [triggerEvent retain];
1512     }
1513 }
1514
1515 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1516 // We can't remove this yet because it's still in use by Mail.
1517 - (NSRect)_selectionRect
1518 {
1519     return [self selectionRect];
1520 }
1521
1522 - (void)_stopAutoscrollTimer
1523 {
1524     NSTimer *timer = _private->autoscrollTimer;
1525     _private->autoscrollTimer = nil;
1526     [_private->autoscrollTriggerEvent release];
1527     _private->autoscrollTriggerEvent = nil;
1528     [timer invalidate];
1529     [timer release];
1530 }
1531
1532 - (void)_autoscroll
1533 {
1534     // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1535     // a mouse up event.
1536     BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);   
1537     if (!isStillDown){
1538         [self _stopAutoscrollTimer];
1539         return;
1540     }
1541
1542     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1543         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1544         modifierFlags:[[NSApp currentEvent] modifierFlags]
1545         timestamp:[NSDate timeIntervalSinceReferenceDate]
1546         windowNumber:[[self window] windowNumber]
1547         context:[[NSApp currentEvent] context]
1548         eventNumber:0 clickCount:0 pressure:0];
1549     [self mouseDragged:fakeEvent];
1550 }
1551
1552 - (BOOL)_canCopy
1553 {
1554     // Copying can be done regardless of whether you can edit.
1555     return [self _hasSelection];
1556 }
1557
1558 - (BOOL)_canCut
1559 {
1560     return [self _hasSelection] && [self _isEditable];
1561 }
1562
1563 - (BOOL)_canDelete
1564 {
1565     return [self _hasSelection] && [self _isEditable];
1566 }
1567
1568 - (BOOL)_canPaste
1569 {
1570     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1571 }
1572
1573 - (BOOL)_canEdit
1574 {
1575     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1576 }
1577
1578 - (BOOL)_canEditRichly
1579 {
1580     return [self _canEdit] && [[self _bridge] isSelectionRichlyEditable];
1581 }
1582
1583 - (BOOL)_canAlterCurrentSelection
1584 {
1585     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1586 }
1587
1588 - (BOOL)_hasSelection
1589 {
1590     return [[self _bridge] selectionState] == WebSelectionStateRange;
1591 }
1592
1593 - (BOOL)_hasSelectionOrInsertionPoint
1594 {
1595     return [[self _bridge] selectionState] != WebSelectionStateNone;
1596 }
1597
1598 - (BOOL)_hasInsertionPoint
1599 {
1600     return [[self _bridge] selectionState] == WebSelectionStateCaret;
1601 }
1602
1603 - (BOOL)_isEditable
1604 {
1605     return [[self _webView] isEditable] || [[self _bridge] isSelectionEditable];
1606 }
1607
1608 - (BOOL)_isSelectionMisspelled
1609 {
1610     NSString *selectedString = [self selectedString];
1611     unsigned length = [selectedString length];
1612     if (length == 0) {
1613         return NO;
1614     }
1615     NSRange range = [[NSSpellChecker sharedSpellChecker] checkSpellingOfString:selectedString
1616                                                                     startingAt:0
1617                                                                       language:nil
1618                                                                           wrap:NO
1619                                                         inSpellDocumentWithTag:[[self _webView] spellCheckerDocumentTag]
1620                                                                      wordCount:NULL];
1621     return range.length == length;
1622 }
1623
1624 - (NSArray *)_guessesForMisspelledSelection
1625 {
1626     ASSERT([[self selectedString] length] != 0);
1627     return [[NSSpellChecker sharedSpellChecker] guessesForWord:[self selectedString]];
1628 }
1629
1630 - (void)_changeSpellingFromMenu:(id)sender
1631 {
1632     ASSERT([[self selectedString] length] != 0);
1633     if ([self _shouldReplaceSelectionWithText:[sender title] givenAction:WebViewInsertActionPasted]) {
1634         [[self _bridge] replaceSelectionWithText:[sender title] selectReplacement:YES smartReplace:NO];
1635     }
1636 }
1637
1638 - (void)_ignoreSpellingFromMenu:(id)sender
1639 {
1640     ASSERT([[self selectedString] length] != 0);
1641     [[NSSpellChecker sharedSpellChecker] ignoreWord:[self selectedString] inSpellDocumentWithTag:[[self _webView] spellCheckerDocumentTag]];
1642 }
1643
1644 - (void)_learnSpellingFromMenu:(id)sender
1645 {
1646     ASSERT([[self selectedString] length] != 0);
1647     [[NSSpellChecker sharedSpellChecker] learnWord:[self selectedString]];
1648 }
1649
1650 - (void)_lookUpInDictionaryFromMenu:(id)sender
1651 {
1652     // This should only be called when there's a selection, but play it safe.
1653     if (![self _hasSelection]) {
1654         return;
1655     }
1656     
1657     // Soft link to dictionary-display function to avoid linking another framework (ApplicationServices/LangAnalysis)
1658     static OSStatus (*__dictionaryServiceWindowShow)(id inWordString, NSRect inWordBoundary, UInt16 inLineDirection) = NULL;
1659     static const struct mach_header *frameworkImageHeader = NULL;
1660     static BOOL lookedForFunction = NO;
1661     if (!lookedForFunction) {
1662         __dictionaryServiceWindowShow = _NSSoftLinkingGetFrameworkFuncPtr(@"ApplicationServices", @"LangAnalysis", "_DCMDictionaryServiceWindowShow", &frameworkImageHeader);
1663         lookedForFunction = YES;
1664     }
1665     if (!__dictionaryServiceWindowShow) {
1666         LOG_ERROR("Couldn't find _DCMDictionaryServiceWindowShow"); 
1667         return;
1668     }
1669     
1670     // FIXME: must check for right-to-left here
1671     NSWritingDirection writingDirection = NSWritingDirectionLeftToRight;
1672     
1673     NSAttributedString *attrString = [self selectedAttributedString];
1674     // FIXME: the dictionary API expects the rect for the first line of selection. Passing
1675     // the rect for the entire selection, as we do here, positions the pop-up window near
1676     // the bottom of the selection rather than at the selected word.
1677     NSRect rect = [self convertRect:[[self _bridge] visibleSelectionRect] toView:nil];
1678     rect.origin = [[self window] convertBaseToScreen:rect.origin];
1679     NSData *data = [attrString RTFFromRange:NSMakeRange(0, [attrString length]) documentAttributes:nil];
1680     (void)__dictionaryServiceWindowShow(data, rect, (writingDirection == NSWritingDirectionRightToLeft) ? 1 : 0);
1681 }
1682
1683 - (BOOL)_transparentBackground
1684 {
1685     return _private->transparentBackground;
1686 }
1687
1688 - (void)_setTransparentBackground:(BOOL)f
1689 {
1690     _private->transparentBackground = f;
1691 }
1692
1693 - (NSImage *)_selectionDraggingImage
1694 {
1695     if ([self _hasSelection]) {
1696         NSImage *dragImage = [[self _bridge] selectionImageForcingWhiteText:NO];
1697         [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1698         return dragImage;
1699     }
1700     return nil;
1701 }
1702
1703 - (NSRect)_selectionDraggingRect
1704 {
1705     // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1706     return [self selectionImageRect];
1707 }
1708
1709 - (BOOL)_canIncreaseSelectionListLevel
1710 {
1711     return ([self _canEditRichly] && [[self _bridge] canIncreaseSelectionListLevel]);
1712 }
1713
1714 - (BOOL)_canDecreaseSelectionListLevel
1715 {
1716     return ([self _canEditRichly] && [[self _bridge] canDecreaseSelectionListLevel]);
1717 }
1718
1719 - (DOMNode *)_increaseSelectionListLevel
1720 {
1721     if (![self _canEditRichly])
1722         return nil;
1723         
1724     WebFrameBridge *bridge = [self _bridge];
1725     return [bridge increaseSelectionListLevel];
1726 }
1727
1728 - (DOMNode *)_increaseSelectionListLevelOrdered
1729 {
1730     if (![self _canEditRichly])
1731         return nil;
1732         
1733     WebFrameBridge *bridge = [self _bridge];
1734     return [bridge increaseSelectionListLevelOrdered];
1735 }
1736
1737 - (DOMNode *)_increaseSelectionListLevelUnordered
1738 {
1739     if (![self _canEditRichly])
1740         return nil;
1741         
1742     WebFrameBridge *bridge = [self _bridge];
1743     return [bridge increaseSelectionListLevelUnordered];
1744 }
1745
1746 - (void)_decreaseSelectionListLevel
1747 {
1748     if (![self _canEditRichly])
1749         return;
1750         
1751     WebFrameBridge *bridge = [self _bridge];
1752     [bridge decreaseSelectionListLevel];
1753 }
1754
1755 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1756 {
1757     if (!_private->highlighters)
1758         _private->highlighters = [[NSMutableDictionary alloc] init];
1759     [_private->highlighters setObject:highlighter forKey:type];
1760 }
1761
1762 - (void)_removeHighlighterOfType:(NSString*)type
1763 {
1764     [_private->highlighters removeObjectForKey:type];
1765 }
1766
1767 - (BOOL)_web_firstResponderCausesFocusDisplay
1768 {
1769     return [self _web_firstResponderIsSelfOrDescendantView] || [[self window] firstResponder] == [self _frameView];
1770 }
1771
1772 - (void)_updateActiveState
1773 {
1774     // This method does the job of updating the view based on the view's firstResponder-ness and
1775     // the window key-ness of the window containing this view. This involves four kinds of 
1776     // drawing updates right now, all handled in WebCore in response to the call over the bridge. 
1777     // 
1778     // The four display attributes are as follows:
1779     // 
1780     // 1. The background color used to draw behind selected content (active | inactive color)
1781     // 2. Caret blinking (blinks | does not blink)
1782     // 3. The drawing of a focus ring around links in web pages.
1783     // 4. Changing the tint of controls from clear to aqua/graphite and vice versa
1784     //
1785     // Also, this is responsible for letting the bridge know if the window has gained or lost focus
1786     // so we can send focus and blur events.
1787
1788     NSWindow *window = [self window];
1789     BOOL windowIsKey = [window isKeyWindow];
1790     BOOL windowOrSheetIsKey = windowIsKey || [[window attachedSheet] isKeyWindow];
1791
1792     BOOL isActive = !_private->resigningFirstResponder && windowIsKey && (_private->descendantBecomingFirstResponder || [self _web_firstResponderCausesFocusDisplay]);
1793     
1794     [[self _bridge] setWindowHasFocus:windowOrSheetIsKey];
1795     [[self _bridge] setIsActive:isActive];
1796 }
1797
1798 - (unsigned)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag
1799 {
1800     return [[self _bridge] markAllMatchesForText:string caseSensitive:caseFlag];
1801 }
1802
1803 - (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
1804 {
1805     [[self _bridge] setMarkedTextMatchesAreHighlighted:newValue];
1806 }
1807
1808 - (BOOL)markedTextMatchesAreHighlighted
1809 {
1810     return [[self _bridge] markedTextMatchesAreHighlighted];
1811 }
1812
1813 - (void)unmarkAllTextMatches
1814 {
1815     return [[self _bridge] unmarkAllTextMatches];
1816 }
1817
1818 - (NSArray *)rectsForTextMatches
1819 {
1820     return [[self _bridge] rectsForTextMatches];
1821 }
1822
1823 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1824 {
1825     ASSERT([self _hasSelection]);
1826     NSArray *types = [self pasteboardTypesForSelection];
1827
1828     // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1829     NSAttributedString *attributedString = [self selectedAttributedString];
1830     NSMutableArray *mutableTypes = nil;
1831     if (![attributedString containsAttachments]) {
1832         mutableTypes = [types mutableCopy];
1833         [mutableTypes removeObject:NSRTFDPboardType];
1834         types = mutableTypes;
1835     }
1836
1837     [pasteboard declareTypes:types owner:nil];
1838     [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1839     [mutableTypes release];
1840 }
1841
1842 - (void)close
1843 {
1844     if (_private->closed)
1845         return;
1846     [self _clearLastHitViewIfSelf];
1847     // FIXME: This is slow; should remove individual observers instead.
1848     [[NSNotificationCenter defaultCenter] removeObserver:self];
1849     [_private->pluginController destroyAllPlugins];
1850     // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1851     [self removeAllToolTips];
1852     _private->closed = YES;
1853 }
1854
1855 @end
1856
1857 @implementation NSView (WebHTMLViewFileInternal)
1858
1859 - (void)_web_setPrintingModeRecursive
1860 {
1861     [_subviews makeObjectsPerformSelector:@selector(_web_setPrintingModeRecursive)];
1862 }
1863
1864 - (void)_web_clearPrintingModeRecursive
1865 {
1866     [_subviews makeObjectsPerformSelector:@selector(_web_clearPrintingModeRecursive)];
1867 }
1868
1869 - (void)_web_layoutIfNeededRecursive
1870 {
1871     [_subviews makeObjectsPerformSelector:@selector(_web_layoutIfNeededRecursive)];
1872 }
1873
1874 - (void)_web_layoutIfNeededRecursive: (NSRect)rect testDirtyRect:(bool)testDirtyRect
1875 {
1876     unsigned index, count;
1877     for (index = 0, count = [(NSArray *)_subviews count]; index < count; index++) {
1878         NSView *subview = [_subviews objectAtIndex:index];
1879         NSRect dirtiedSubviewRect = [subview convertRect: rect fromView: self];
1880         [subview _web_layoutIfNeededRecursive: dirtiedSubviewRect testDirtyRect:testDirtyRect];
1881     }
1882 }
1883
1884 @end
1885
1886 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
1887
1888 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
1889 {
1890     if (object == nil) {
1891         [self removeObjectForKey:key];
1892     } else {
1893         [self setObject:object forKey:key];
1894     }
1895 }
1896
1897 @end
1898
1899 // The following is a workaround for
1900 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
1901 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
1902 // Since the category will be searched before the real class, we'll prevent the flag from being
1903 // set on the tool tip panel.
1904
1905 @interface NSToolTipPanel : NSPanel
1906 @end
1907
1908 @interface NSToolTipPanel (WebHTMLViewFileInternal)
1909 @end
1910
1911 @implementation NSToolTipPanel (WebHTMLViewFileInternal)
1912
1913 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
1914 {
1915     // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
1916 }
1917
1918 @end
1919
1920 @interface NSArray (WebHTMLView)
1921 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
1922 @end
1923
1924 @implementation WebHTMLView
1925
1926 + (void)initialize
1927 {
1928     [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] 
1929                              returnTypes:[[self class] _insertablePasteboardTypes]];
1930     _NSInitializeKillRing();
1931 }
1932
1933 - (void)_resetCachedWebPreferences:(NSNotification *)ignored
1934 {
1935     WebPreferences *preferences = [[self _webView] preferences];
1936     // Check for nil because we might not yet have an associated webView when this is called
1937     if (preferences == nil) {
1938         preferences = [WebPreferences standardPreferences];
1939     }
1940     _private->showsURLsInToolTips = [preferences showsURLsInToolTips];
1941 }
1942
1943 - (id)initWithFrame:(NSRect)frame
1944 {
1945     self = [super initWithFrame:frame];
1946     if (!self)
1947         return nil;
1948     
1949     // Make all drawing go through us instead of subviews.
1950     if (NSAppKitVersionNumber >= 711) {
1951         [self _setDrawsOwnDescendants:YES];
1952     }
1953     
1954     _private = [[WebHTMLViewPrivate alloc] init];
1955
1956     _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
1957     _private->needsLayout = YES;
1958     [self _resetCachedWebPreferences:nil];
1959     [[NSNotificationCenter defaultCenter] 
1960             addObserver:self selector:@selector(_resetCachedWebPreferences:) 
1961                    name:WebPreferencesChangedNotification object:nil];
1962     
1963     return self;
1964 }
1965
1966 - (void)dealloc
1967 {
1968     // We can't assert that close has already been called because
1969     // this view can be removed from it's superview, even though
1970     // it could be needed later, so close if needed.
1971     [self close];
1972     [_private release];
1973     _private = nil;
1974     [super dealloc];
1975 }
1976
1977 - (void)finalize
1978 {
1979     // We can't assert that close has already been called because
1980     // this view can be removed from it's superview, even though
1981     // it could be needed later, so close if needed.
1982     [self close];
1983     [super finalize];
1984 }
1985
1986 - (IBAction)takeFindStringFromSelection:(id)sender
1987 {
1988     if (![self _hasSelection]) {
1989         NSBeep();
1990         return;
1991     }
1992
1993     [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
1994 }
1995
1996 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
1997 {
1998     [pasteboard declareTypes:types owner:nil];
1999     [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2000     return YES;
2001 }
2002
2003 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2004 {
2005     if ([[self _bridge] isSelectionRichlyEditable])
2006         [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
2007     else
2008         [self _pasteAsPlainTextWithPasteboard:pasteboard];
2009     return YES;
2010 }
2011
2012 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2013 {
2014     if (sendType != nil && [[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]) {
2015         return self;
2016     } else if (returnType != nil && [[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2017         return self;
2018     }
2019     return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2020 }
2021
2022 - (void)selectAll:(id)sender
2023 {
2024     [self selectAll];
2025 }
2026
2027 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2028 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will us the
2029 // selector centerSelectionInVisibleArea. We'll leave this old selector in place for two reasons:
2030 // (1) compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2031 // might be using the jumpToSelection: selector, and we don't want to break them.
2032 - (void)jumpToSelection:(id)sender
2033 {
2034     [self centerSelectionInVisibleArea:sender];
2035 }
2036
2037 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item 
2038 {
2039     SEL action = [item action];
2040     WebFrameBridge *bridge = [self _bridge];
2041
2042     if (action == @selector(changeBaseWritingDirection:) // FIXME: check menu item based on writing direction
2043             || action == @selector(changeSpelling:)
2044             || action == @selector(_changeSpellingFromMenu:)
2045             || action == @selector(checkSpelling:)
2046             || action == @selector(complete:)
2047             || action == @selector(deleteBackward:)
2048             || action == @selector(deleteBackwardByDecomposingPreviousCharacter:)
2049             || action == @selector(deleteForward:)
2050             || action == @selector(deleteToBeginningOfLine:)
2051             || action == @selector(deleteToBeginningOfParagraph:)
2052             || action == @selector(deleteToEndOfLine:)
2053             || action == @selector(deleteToEndOfParagraph:)
2054             || action == @selector(deleteToMark:)
2055             || action == @selector(deleteWordBackward:)
2056             || action == @selector(deleteWordForward:)
2057             || action == @selector(insertBacktab:)
2058             || action == @selector(insertLineBreak:)
2059             || action == @selector(insertNewline:)
2060             || action == @selector(insertNewlineIgnoringFieldEditor:)
2061             || action == @selector(insertParagraphSeparator:)
2062             || action == @selector(insertTab:)
2063             || action == @selector(insertTabIgnoringFieldEditor:)
2064             || action == @selector(moveBackward:)
2065             || action == @selector(moveBackwardAndModifySelection:)
2066             || action == @selector(moveDown:)
2067             || action == @selector(moveDownAndModifySelection:)
2068             || action == @selector(moveForward:)
2069             || action == @selector(moveForwardAndModifySelection:)
2070             || action == @selector(moveLeft:)
2071             || action == @selector(moveLeftAndModifySelection:)
2072             || action == @selector(moveParagraphBackwardAndModifySelection:)
2073             || action == @selector(moveParagraphForwardAndModifySelection:)
2074             || action == @selector(moveRight:)
2075             || action == @selector(moveRightAndModifySelection:)
2076             || action == @selector(moveToBeginningOfDocument:)
2077             || action == @selector(moveToBeginningOfDocumentAndModifySelection:)
2078             || action == @selector(moveToBeginningOfSentence:)
2079             || action == @selector(moveToBeginningOfSentenceAndModifySelection:)
2080             || action == @selector(moveToBeginningOfLine:)
2081             || action == @selector(moveToBeginningOfLineAndModifySelection:)
2082             || action == @selector(moveToBeginningOfParagraph:)
2083             || action == @selector(moveToBeginningOfParagraphAndModifySelection:)
2084             || action == @selector(moveToEndOfDocument:)
2085             || action == @selector(moveToEndOfDocumentAndModifySelection:)
2086             || action == @selector(moveToEndOfSentence:)
2087             || action == @selector(moveToEndOfSentenceAndModifySelection:)
2088             || action == @selector(moveToEndOfLine:)
2089             || action == @selector(moveToEndOfLineAndModifySelection:)
2090             || action == @selector(moveToEndOfParagraph:)
2091             || action == @selector(moveToEndOfParagraphAndModifySelection:)
2092             || action == @selector(moveUp:)
2093             || action == @selector(moveUpAndModifySelection:)
2094             || action == @selector(moveWordBackward:)
2095             || action == @selector(moveWordBackwardAndModifySelection:)
2096             || action == @selector(moveWordForward:)
2097             || action == @selector(moveWordForwardAndModifySelection:)
2098             || action == @selector(moveWordLeft:)
2099             || action == @selector(moveWordLeftAndModifySelection:)
2100             || action == @selector(moveWordRight:)
2101             || action == @selector(moveWordRightAndModifySelection:)
2102             || action == @selector(pageDown:)
2103             || action == @selector(pageDownAndModifySelection:)
2104             || action == @selector(pageUp:)
2105             || action == @selector(pageUpAndModifySelection:)
2106             || action == @selector(pasteFont:)
2107             || action == @selector(showGuessPanel:)
2108             || action == @selector(toggleBaseWritingDirection:)
2109             || action == @selector(transpose:)
2110             || action == @selector(yank:)
2111             || action == @selector(yankAndSelect:)) {
2112         return [self _canEdit];
2113     } else if (action == @selector(alignCenter:)
2114             || action == @selector(alignLeft:)
2115             || action == @selector(alignJustified:)
2116             || action == @selector(alignRight:)
2117             || action == @selector(changeAttributes:)
2118             || action == @selector(changeColor:)        
2119             || action == @selector(changeFont:)) {
2120         return [self _canEditRichly];
2121     } else if (action == @selector(capitalizeWord:)
2122                || action == @selector(lowercaseWord:)
2123                || action == @selector(uppercaseWord:)) {
2124         return [self _hasSelection] && [self _isEditable];
2125     } else if (action == @selector(centerSelectionInVisibleArea:)
2126                || action == @selector(jumpToSelection:)
2127                || action == @selector(copyFont:)
2128                || action == @selector(setMark:)) {
2129         return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2130     } else if (action == @selector(changeDocumentBackgroundColor:)) {
2131         return [[self _webView] isEditable] && [self _canEditRichly];
2132     } else if (action == @selector(copy:)) {
2133         return [bridge mayDHTMLCopy] || [self _canCopy];
2134     } else if (action == @selector(cut:)) {
2135         return [bridge mayDHTMLCut] || [self _canDelete];
2136     } else if (action == @selector(delete:)) {
2137         return [self _canDelete];
2138     } else if (action == @selector(_ignoreSpellingFromMenu:)
2139             || action == @selector(_learnSpellingFromMenu:)
2140             || action == @selector(takeFindStringFromSelection:)) {
2141         return [self _hasSelection];
2142     } else if (action == @selector(paste:) || action == @selector(pasteAsPlainText:)) {
2143         return [bridge mayDHTMLPaste] || [self _canPaste];
2144     } else if (action == @selector(pasteAsRichText:)) {
2145         return [bridge mayDHTMLPaste] || ([self _canPaste] && [[self _bridge] isSelectionRichlyEditable]);
2146     } else if (action == @selector(performFindPanelAction:)) {
2147         // FIXME: Not yet implemented.
2148         return NO;
2149     } else if (action == @selector(selectToMark:)
2150             || action == @selector(swapWithMark:)) {
2151         return [self _hasSelectionOrInsertionPoint] && [[self _bridge] markDOMRange] != nil;
2152     } else if (action == @selector(subscript:)) {
2153         NSMenuItem *menuItem = (NSMenuItem *)item;
2154         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2155             DOMCSSStyleDeclaration *style = [self _emptyStyle];
2156             [style setVerticalAlign:@"sub"];
2157             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2158         }
2159         return [self _canEditRichly];
2160     } else if (action == @selector(superscript:)) {
2161         NSMenuItem *menuItem = (NSMenuItem *)item;
2162         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2163             DOMCSSStyleDeclaration *style = [self _emptyStyle];
2164             [style setVerticalAlign:@"super"];
2165             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2166         }
2167         return [self _canEditRichly];
2168     } else if (action == @selector(underline:)) {
2169         NSMenuItem *menuItem = (NSMenuItem *)item;
2170         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2171             DOMCSSStyleDeclaration *style = [self _emptyStyle];
2172             [style setProperty:@"-khtml-text-decorations-in-effect" :@"underline" :@""];
2173             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2174         }
2175         return [self _canEditRichly];
2176     } else if (action == @selector(unscript:)) {
2177         NSMenuItem *menuItem = (NSMenuItem *)item;
2178         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2179             DOMCSSStyleDeclaration *style = [self _emptyStyle];
2180             [style setVerticalAlign:@"baseline"];
2181             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2182         }
2183         return [self _canEditRichly];
2184     } else if (action == @selector(_lookUpInDictionaryFromMenu:)) {
2185         return [self _hasSelection];
2186     }
2187     
2188     return YES;
2189 }
2190
2191 - (BOOL)acceptsFirstResponder
2192 {
2193     // Don't accept first responder when we first click on this view.
2194     // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2195     // Do accept first responder at any other time, for example from keyboard events,
2196     // or from calls back from WebCore once we begin mouse-down event handling.
2197     NSEvent *event = [NSApp currentEvent];
2198     if ([event type] == NSLeftMouseDown
2199             && !_private->handlingMouseDownEvent
2200             && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2201         return NO;
2202     }
2203     return YES;
2204 }
2205
2206 - (BOOL)maintainsInactiveSelection
2207 {
2208     // This method helps to determine whether the WebHTMLView should maintain
2209     // an inactive selection when it's not first responder.
2210     // Traditionally, these views have not maintained such selections,
2211     // clearing them when the view was not first responder. However,
2212     // to fix bugs like this one:
2213     // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even 
2214     //                            when they're not firstResponder"
2215     // it was decided to add a switch to act more like an NSTextView.
2216     id nextResponder = [[self window] _newFirstResponderAfterResigning];
2217
2218     // Predict the case where we are losing first responder status only to
2219     // gain it back again.  Want to keep the selection in that case.
2220     if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2221         id contentView = [nextResponder contentView];
2222         if (contentView) {
2223             nextResponder = contentView;
2224         }
2225     }
2226     if ([nextResponder isKindOfClass:[NSClipView class]]) {
2227         id documentView = [nextResponder documentView];
2228         if (documentView) {
2229             nextResponder = documentView;
2230         }
2231     }
2232
2233     if (nextResponder == self)
2234         return YES;
2235
2236     return [[self _webView] maintainsInactiveSelection] || [[self _bridge] isSelectionEditable];
2237 }
2238
2239 - (void)addMouseMovedObserver
2240 {
2241     if (!_private->dataSource || ![self _isTopHTMLView])
2242         return;
2243
2244     // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2245     if (!([[self window] isKeyWindow] || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]))
2246         return;
2247
2248     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2249         name:WKMouseMovedNotification() object:nil];
2250     [self _frameOrBoundsChanged];
2251 }
2252
2253 - (void)removeMouseMovedObserver
2254 {
2255     // Don't remove the observer if we're running the Dashboard.
2256     // FIXME: Right for the windowDidResignKey: case, but wrong for the viewWillMoveToWindow: case.
2257     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2258         return;
2259
2260     [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2261     [[NSNotificationCenter defaultCenter] removeObserver:self
2262         name:WKMouseMovedNotification() object:nil];
2263 }
2264
2265 - (void)addSuperviewObservers
2266 {
2267     // We watch the bounds of our superview, so that we can do a layout when the size
2268     // of the superview changes. This is different from other scrollable things that don't
2269     // need this kind of thing because their layout doesn't change.
2270     
2271     // We need to pay attention to both height and width because our "layout" has to change
2272     // to extend the background the full height of the space and because some elements have
2273     // sizes that are based on the total size of the view.
2274     
2275     NSView *superview = [self superview];
2276     if (superview && [self window]) {
2277         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
2278             name:NSViewFrameDidChangeNotification object:superview];
2279         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
2280             name:NSViewBoundsDidChangeNotification object:superview];
2281
2282         // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2283         // It will check the current size/scroll against the previous layout's size/scroll.  We need to
2284         // do this here to catch the case where the WebView is laid out at one size, removed from its
2285         // window, resized, and inserted into another window.  Our frame/bounds changed notifications
2286         // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2287         [self _frameOrBoundsChanged];
2288     }
2289 }
2290
2291 - (void)removeSuperviewObservers
2292 {
2293     NSView *superview = [self superview];
2294     if (superview && [self window]) {
2295         [[NSNotificationCenter defaultCenter] removeObserver:self
2296             name:NSViewFrameDidChangeNotification object:superview];
2297         [[NSNotificationCenter defaultCenter] removeObserver:self
2298             name:NSViewBoundsDidChangeNotification object:superview];
2299     }
2300 }
2301
2302 - (void)addWindowObservers
2303 {
2304     NSWindow *window = [self window];
2305     if (window) {
2306         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:)
2307             name:NSWindowDidBecomeKeyNotification object:nil];
2308         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:)
2309             name:NSWindowDidResignKeyNotification object:nil];
2310         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:)
2311             name:NSWindowWillCloseNotification object:window];
2312     }
2313 }
2314
2315 - (void)removeWindowObservers
2316 {
2317     NSWindow *window = [self window];
2318     if (window) {
2319         [[NSNotificationCenter defaultCenter] removeObserver:self
2320             name:NSWindowDidBecomeKeyNotification object:nil];
2321         [[NSNotificationCenter defaultCenter] removeObserver:self
2322             name:NSWindowDidResignKeyNotification object:nil];
2323         [[NSNotificationCenter defaultCenter] removeObserver:self
2324             name:NSWindowWillCloseNotification object:window];
2325     }
2326 }
2327
2328 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2329 {
2330     [self removeSuperviewObservers];
2331 }
2332
2333 - (void)viewDidMoveToSuperview
2334 {
2335     // Do this here in case the text size multiplier changed when a non-HTML
2336     // view was installed.
2337     if ([self superview] != nil) {
2338         [self _updateTextSizeMultiplier];
2339         [self addSuperviewObservers];
2340     }
2341 }
2342
2343 - (void)viewWillMoveToWindow:(NSWindow *)window
2344 {
2345     // Don't do anything if we aren't initialized.  This happens
2346     // when decoding a WebView.  When WebViews are decoded their subviews
2347     // are created by initWithCoder: and so won't be normally
2348     // initialized.  The stub views are discarded by WebView.
2349     if (_private) {
2350         // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2351         [self removeMouseMovedObserver];
2352         [self removeWindowObservers];
2353         [self removeSuperviewObservers];
2354         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
2355     
2356         [[self _pluginController] stopAllPlugins];
2357     }
2358 }
2359
2360 - (void)viewDidMoveToWindow
2361 {
2362     // Don't do anything if we aren't initialized.  This happens
2363     // when decoding a WebView.  When WebViews are decoded their subviews
2364     // are created by initWithCoder: and so won't be normally
2365     // initialized.  The stub views are discarded by WebView.
2366     if (_private) {
2367         [self _stopAutoscrollTimer];
2368         if ([self window]) {
2369             _private->lastScrollPosition = [[self superview] bounds].origin;
2370             [self addWindowObservers];
2371             [self addSuperviewObservers];
2372             [self addMouseMovedObserver];
2373
2374             // Schedule this update, rather than making the call right now.
2375             // The reason is that placing the caret in the just-installed view requires
2376             // the HTML/XML document to be available on the WebCore side, but it is not
2377             // at the time this code is running. However, it will be there on the next
2378             // crank of the run loop. Doing this helps to make a blinking caret appear 
2379             // in a new, empty window "automatic".
2380             [self performSelector:@selector(_updateActiveState) withObject:nil afterDelay:0];
2381
2382             [[self _pluginController] startAllPlugins];
2383     
2384             _private->lastScrollPosition = NSZeroPoint;
2385         }
2386     }
2387 }
2388
2389 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2390 {
2391     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2392 }
2393
2394 - (void)viewDidMoveToHostWindow
2395 {
2396     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2397 }
2398
2399
2400 - (void)addSubview:(NSView *)view
2401 {
2402     [super addSubview:view];
2403
2404     if ([WebPluginController isPlugInView:view]) {
2405         [[self _pluginController] addPlugin:view];
2406     }
2407 }
2408
2409 - (void)willRemoveSubview:(NSView *)subview
2410 {
2411     if ([WebPluginController isPlugInView:subview])
2412         [[self _pluginController] destroyPlugin:subview];
2413     [super willRemoveSubview:subview];
2414 }
2415
2416 - (void)reapplyStyles
2417 {
2418     if (!_private->needsToApplyStyles) {
2419         return;
2420     }
2421     
2422 #ifdef _KWQ_TIMING        
2423     double start = CFAbsoluteTimeGetCurrent();
2424 #endif
2425
2426     [[self _bridge] reapplyStylesForDeviceType:
2427         _private->printing ? WebCoreDevicePrinter : WebCoreDeviceScreen];
2428     
2429 #ifdef _KWQ_TIMING        
2430     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2431     LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2432 #endif
2433
2434     _private->needsToApplyStyles = NO;
2435 }
2436
2437 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2438 // minPageWidth==0 implies a non-printing layout
2439 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
2440 {
2441     [self reapplyStyles];
2442     
2443     // Ensure that we will receive mouse move events.  Is this the best place to put this?
2444     [[self window] setAcceptsMouseMovedEvents: YES];
2445     WKSetNSWindowShouldPostEventNotifications([self window], YES);
2446
2447     if (!_private->needsLayout) {
2448         return;
2449     }
2450
2451 #ifdef _KWQ_TIMING        
2452     double start = CFAbsoluteTimeGetCurrent();
2453 #endif
2454
2455     LOG(View, "%@ doing layout", self);
2456
2457     if (minPageWidth > 0.0) {
2458         [[self _bridge] forceLayoutWithMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
2459     } else {
2460         [[self _bridge] forceLayoutAdjustingViewSize:adjustViewSize];
2461     }
2462     _private->needsLayout = NO;
2463     
2464     if (!_private->printing) {
2465         // get size of the containing dynamic scrollview, so
2466         // appearance and disappearance of scrollbars will not show up
2467         // as a size change
2468         NSSize newLayoutFrameSize = [[[self superview] superview] frame].size;
2469         if (_private->laidOutAtLeastOnce && !NSEqualSizes(_private->lastLayoutFrameSize, newLayoutFrameSize)) {
2470             [[self _bridge] sendResizeEvent];
2471             if ([[self _bridge] needsLayout])
2472                 [[self _bridge] forceLayoutAdjustingViewSize:NO];
2473         }
2474         _private->laidOutAtLeastOnce = YES;
2475         _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
2476         _private->lastLayoutFrameSize = newLayoutFrameSize;
2477     }
2478
2479 #ifdef _KWQ_TIMING        
2480     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2481     LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
2482 #endif
2483 }
2484
2485 - (void)layout
2486 {
2487     [self layoutToMinimumPageWidth:0.0 maximumPageWidth:0.0 adjustingViewSize:NO];
2488 }
2489
2490 - (NSMenu *)menuForEvent:(NSEvent *)event
2491 {
2492     [_private->compController endRevertingChange:NO moveLeft:NO];
2493
2494     _private->handlingMouseDownEvent = YES;
2495     BOOL handledEvent = [[self _bridge] sendContextMenuEvent:event];
2496     _private->handlingMouseDownEvent = NO;
2497     if (handledEvent)
2498         return nil;
2499
2500     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2501     NSDictionary *element = [self elementAtPoint:point];
2502     return [[self _webView] _menuForElement:element defaultItems:nil];
2503 }
2504
2505 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
2506 {
2507     if (![string length])
2508         return NO;
2509
2510     return [[self _bridge] searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag];
2511 }
2512
2513 - (void)deselectText
2514 {
2515     [[self _bridge] deselectText];
2516 }
2517
2518 - (BOOL)isOpaque
2519 {
2520     return [[self _webView] drawsBackground];
2521 }
2522
2523 - (void)setNeedsDisplay:(BOOL)flag
2524 {
2525     LOG(View, "%@ flag = %d", self, (int)flag);
2526     [super setNeedsDisplay: flag];
2527 }
2528
2529 - (void)setNeedsLayout: (BOOL)flag
2530 {
2531     LOG(View, "%@ flag = %d", self, (int)flag);
2532     _private->needsLayout = flag;
2533 }
2534
2535
2536 - (void)setNeedsToApplyStyles: (BOOL)flag
2537 {
2538     LOG(View, "%@ flag = %d", self, (int)flag);
2539     _private->needsToApplyStyles = flag;
2540 }
2541
2542 - (void)drawSingleRect:(NSRect)rect
2543 {
2544     [NSGraphicsContext saveGraphicsState];
2545     NSRectClip(rect);
2546         
2547     ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
2548
2549     [(WebClipView *)[self superview] setAdditionalClip:rect];
2550
2551     NS_DURING {
2552         if ([self _transparentBackground]) {
2553             [[NSColor clearColor] set];
2554             NSRectFill (rect);
2555         }
2556         
2557         [[self _bridge] drawRect:rect];        
2558         WebView *webView = [self _webView];
2559         [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
2560         [(WebClipView *)[self superview] resetAdditionalClip];
2561
2562         [NSGraphicsContext restoreGraphicsState];
2563     } NS_HANDLER {
2564         [(WebClipView *)[self superview] resetAdditionalClip];
2565         [NSGraphicsContext restoreGraphicsState];
2566         LOG_ERROR("Exception caught while drawing: %@", localException);
2567         [localException raise];
2568     } NS_ENDHANDLER
2569 }
2570
2571 - (void)drawRect:(NSRect)rect
2572 {
2573     LOG(View, "%@ drawing", self);
2574
2575     const NSRect *rects;
2576     WebNSInteger count;
2577     [self getRectsBeingDrawn:&rects count:&count];
2578
2579     BOOL subviewsWereSetAside = _private->subviewsSetAside;
2580     if (subviewsWereSetAside)
2581         [self _restoreSubviews];
2582
2583 #ifdef _KWQ_TIMING
2584     double start = CFAbsoluteTimeGetCurrent();
2585 #endif
2586
2587     // If count == 0 here, use the rect passed in for drawing. This is a workaround for:
2588     // <rdar://problem/3908282> REGRESSION (Mail): No drag image dragging selected text in Blot and Mail
2589     // The reason for the workaround is that this method is called explicitly from the code
2590     // to generate a drag image, and at that time, getRectsBeingDrawn:count: will return a zero count.
2591     const int cRectThreshold = 10;
2592     const float cWastedSpaceThreshold = 0.75f;
2593     BOOL useUnionedRect = (count <= 1) || (count > cRectThreshold);
2594     if (!useUnionedRect) {
2595         // Attempt to guess whether or not we should use the unioned rect or the individual rects.
2596         // We do this by computing the percentage of "wasted space" in the union.  If that wasted space
2597         // is too large, then we will do individual rect painting instead.
2598         float unionPixels = (rect.size.width * rect.size.height);
2599         float singlePixels = 0;
2600         for (int i = 0; i < count; ++i)
2601             singlePixels += rects[i].size.width * rects[i].size.height;
2602         float wastedSpace = 1 - (singlePixels / unionPixels);
2603         if (wastedSpace <= cWastedSpaceThreshold)
2604             useUnionedRect = YES;
2605     }
2606     
2607     if (useUnionedRect)
2608         [self drawSingleRect:rect];
2609     else
2610         for (int i = 0; i < count; ++i)
2611             [self drawSingleRect:rects[i]];
2612
2613 #ifdef _KWQ_TIMING
2614     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2615     LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
2616 #endif
2617
2618     if (subviewsWereSetAside)
2619         [self _setAsideSubviews];
2620 }
2621
2622 // Turn off the additional clip while computing our visibleRect.
2623 - (NSRect)visibleRect
2624 {
2625     if (!([[self superview] isKindOfClass:[WebClipView class]]))
2626         return [super visibleRect];
2627         
2628     WebClipView *clipView = (WebClipView *)[self superview];
2629
2630     BOOL hasAdditionalClip = [clipView hasAdditionalClip];
2631     if (!hasAdditionalClip) {
2632         return [super visibleRect];
2633     }
2634     
2635     NSRect additionalClip = [clipView additionalClip];
2636     [clipView resetAdditionalClip];
2637     NSRect visibleRect = [super visibleRect];
2638     [clipView setAdditionalClip:additionalClip];
2639     return visibleRect;
2640 }
2641
2642 - (BOOL)isFlipped 
2643 {
2644     return YES;
2645 }
2646
2647 - (void)windowDidBecomeKey:(NSNotification *)notification
2648 {
2649     NSWindow *keyWindow = [notification object];
2650
2651     if (keyWindow == [self window])
2652         [self addMouseMovedObserver];
2653
2654     if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
2655         [self _updateActiveState];
2656 }
2657
2658 - (void)windowDidResignKey:(NSNotification *)notification
2659 {
2660     NSWindow *formerKeyWindow = [notification object];
2661
2662     if (formerKeyWindow == [self window])
2663         [self removeMouseMovedObserver];
2664
2665     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
2666         [self _updateActiveState];
2667         [_private->compController endRevertingChange:NO moveLeft:NO];
2668     }
2669 }
2670
2671 - (void)windowWillClose:(NSNotification *)notification
2672 {
2673     [_private->compController endRevertingChange:NO moveLeft:NO];
2674     [[self _pluginController] destroyAllPlugins];
2675 }
2676
2677 - (void)scrollWheel:(NSEvent *)event
2678 {
2679     [self retain];
2680     
2681     if (![[self _bridge] sendScrollWheelEvent:event]) {
2682         [[self nextResponder] scrollWheel:event];
2683     }    
2684     
2685     [self release];
2686 }
2687
2688 - (BOOL)_isSelectionEvent:(NSEvent *)event
2689 {
2690     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2691     return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
2692 }
2693
2694 - (void)_setMouseDownEvent:(NSEvent *)event
2695 {
2696     ASSERT([event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
2697
2698     if (event == _private->mouseDownEvent) {
2699         return;
2700     }
2701
2702     [event retain];
2703     [_private->mouseDownEvent release];
2704     _private->mouseDownEvent = event;
2705
2706     [_private->firstResponderTextViewAtMouseDownTime release];
2707     
2708     // The only code that checks this ivar only cares about NSTextViews. The code used to be more general,
2709     // but it caused reference cycles leading to world leaks (see 4557386). We should be able to eliminate
2710     // firstResponderTextViewAtMouseDownTime entirely when all the form controls are native widgets, because 
2711     // the only caller (in WebCore) will be unnecessary.
2712     NSResponder *firstResponder = [[self window] firstResponder];
2713     if ([firstResponder isKindOfClass:[NSTextView class]])
2714         _private->firstResponderTextViewAtMouseDownTime = [firstResponder retain];
2715     else
2716         _private->firstResponderTextViewAtMouseDownTime = nil;
2717 }
2718
2719 - (BOOL)acceptsFirstMouse:(NSEvent *)event
2720 {
2721     NSView *hitView = [self _hitViewForEvent:event];
2722     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2723     [hitHTMLView _setMouseDownEvent:event];
2724     
2725     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
2726         return YES;
2727     
2728     if (hitHTMLView != nil) {
2729         [[hitHTMLView _bridge] setActivationEventNumber:[event eventNumber]];
2730         return [hitHTMLView _isSelectionEvent:event] ? [[hitHTMLView _bridge] eventMayStartDrag:event] : NO;
2731     } else {
2732         return [hitView acceptsFirstMouse:event];
2733     }
2734 }
2735
2736 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
2737 {
2738     NSView *hitView = [self _hitViewForEvent:event];
2739     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2740     if (hitHTMLView != nil) {
2741         [hitHTMLView _setMouseDownEvent:event];
2742         return [hitHTMLView _isSelectionEvent:event] ? [[hitHTMLView _bridge] eventMayStartDrag:event] : NO;
2743     } else {
2744         return [hitView shouldDelayWindowOrderingForEvent:event];
2745     }
2746 }
2747
2748 - (void)mouseDown:(NSEvent *)event
2749 {
2750     [self retain];
2751
2752     _private->handlingMouseDownEvent = YES;
2753
2754     // Record the mouse down position so we can determine drag hysteresis.
2755     [self _setMouseDownEvent:event];
2756
2757     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2758     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2759         goto done;
2760
2761     [_private->compController endRevertingChange:NO moveLeft:NO];
2762
2763     // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
2764     // We don't want to pass them along to KHTML a second time.
2765     if (!([event modifierFlags] & NSControlKeyMask)) {
2766         _private->ignoringMouseDraggedEvents = NO;
2767
2768         // Don't do any mouseover while the mouse is down.
2769         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
2770
2771         // Let KHTML get a chance to deal with the event. This will call back to us
2772         // to start the autoscroll timer if appropriate.
2773         [[self _bridge] mouseDown:event];
2774     }
2775
2776 done:
2777     [_private->firstResponderTextViewAtMouseDownTime release];
2778     _private->firstResponderTextViewAtMouseDownTime = nil;
2779
2780     _private->handlingMouseDownEvent = NO;
2781     
2782     [self release];
2783 }
2784
2785 - (void)dragImage:(NSImage *)dragImage
2786                at:(NSPoint)at
2787            offset:(NSSize)offset
2788             event:(NSEvent *)event
2789        pasteboard:(NSPasteboard *)pasteboard
2790            source:(id)source
2791         slideBack:(BOOL)slideBack
2792 {
2793     [self _stopAutoscrollTimer];
2794
2795     WebHTMLView *topHTMLView = [self _topHTMLView];
2796     if (self != topHTMLView) {
2797         [topHTMLView dragImage:dragImage at:[self convertPoint:at toView:topHTMLView]
2798             offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
2799         return;
2800     }
2801
2802     WebView *webView = [self _webView];
2803
2804     _private->initiatedDrag = YES;
2805     [webView _setInitiatedDrag:YES];
2806
2807     // Retain this view during the drag because it may be released before the drag ends.
2808     [self retain];
2809
2810     id UIDelegate = [webView UIDelegate];
2811     // If a delegate takes over the drag but never calls draggedImage: endedAt:, we'll leak the WebHTMLView.
2812     if ([UIDelegate respondsToSelector:@selector(webView:dragImage:at:offset:event:pasteboard:source:slideBack:forView:)])
2813         [UIDelegate webView:webView dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack forView:self];
2814     else
2815         [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
2816 }
2817
2818 - (void)mouseDragged:(NSEvent *)event
2819 {
2820     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2821     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2822         return;
2823
2824     [self retain];
2825
2826     if (!_private->ignoringMouseDraggedEvents)
2827         [[self _bridge] mouseDragged:event];
2828
2829     [self release];
2830 }
2831
2832 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
2833 {
2834     ASSERT([self _isTopHTMLView]);
2835
2836     if (_private->webCoreDragOp == NSDragOperationNone)
2837         return NSDragOperationGeneric | NSDragOperationCopy;
2838     return _private->webCoreDragOp;
2839 }
2840
2841 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
2842 {
2843     ASSERT([self _isTopHTMLView]);
2844
2845     NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
2846     NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + _private->dragOffset.x, windowImageLoc.y + _private->dragOffset.y);
2847     [[self _bridge] dragSourceMovedTo:windowMouseLoc];
2848 }
2849
2850 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
2851 {
2852     ASSERT(![self _webView] || [self _isTopHTMLView]);
2853
2854     NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
2855     NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + _private->dragOffset.x, windowImageLoc.y + _private->dragOffset.y);
2856     [[self _bridge] dragSourceEndedAt:windowMouseLoc operation:operation];
2857
2858     _private->initiatedDrag = NO;
2859     [[self _webView] _setInitiatedDrag:NO];
2860     
2861     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
2862     _private->ignoringMouseDraggedEvents = YES;
2863     
2864     // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
2865     // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
2866     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2867                                             location:windowMouseLoc
2868                                        modifierFlags:[[NSApp currentEvent] modifierFlags]
2869                                            timestamp:[NSDate timeIntervalSinceReferenceDate]
2870                                         windowNumber:[[self window] windowNumber]
2871                                              context:[[NSApp currentEvent] context]
2872                                          eventNumber:0 clickCount:0 pressure:0];
2873     [self mouseUp:fakeEvent]; // This will also update the mouseover state.
2874     
2875     // Balance the previous retain from when the drag started.
2876     [self release];
2877 }
2878
2879 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
2880 {
2881     ASSERT([self _isTopHTMLView]);
2882     ASSERT(_private->draggingImageURL);
2883
2884     NSFileWrapper *wrapper = [[self _dataSource] _fileWrapperForURL:_private->draggingImageURL];
2885     if (wrapper == nil) {
2886         LOG_ERROR("Failed to create image file. Did the source image change while dragging? (<rdar://problem/4244861>)");
2887         return nil;
2888     }
2889
2890     // FIXME: Report an error if we fail to create a file.
2891     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
2892     path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
2893     if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
2894         LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
2895
2896     return [NSArray arrayWithObject:[path lastPathComponent]];
2897 }
2898
2899 - (void)mouseUp:(NSEvent *)event
2900 {
2901     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2902     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2903         return;
2904
2905     [self retain];
2906
2907     [self _stopAutoscrollTimer];
2908     [[self _bridge] mouseUp:event];
2909     [self _updateMouseoverWithFakeEvent];
2910
2911     [self release];
2912 }
2913
2914 - (void)mouseMovedNotification:(NSNotification *)notification
2915 {
2916     [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
2917 }
2918
2919 // returning YES from this method is the way we tell AppKit that it is ok for this view
2920 // to be in the key loop even when "tab to all controls" is not on.
2921 - (BOOL)needsPanelToBecomeKey
2922 {
2923     return YES;
2924 }
2925
2926 - (NSView *)nextValidKeyView
2927 {
2928     NSView *view = nil;
2929     BOOL lookInsideWebFrameViews = YES;
2930     if ([self isHiddenOrHasHiddenAncestor]) {
2931         lookInsideWebFrameViews = NO;
2932     } else if ([self _frame] == [[self _webView] mainFrame]) {
2933         // Check for case where first responder is last frame in a frameset, and we are
2934         // the top-level documentView.
2935         NSResponder *firstResponder = [[self window] firstResponder];
2936         if ((firstResponder != self) && [firstResponder isKindOfClass:[WebHTMLView class]] && ([(NSView *)firstResponder nextKeyView] == nil)) {
2937             lookInsideWebFrameViews = NO;
2938         }
2939     }
2940     
2941     if (lookInsideWebFrameViews) {
2942         view = [[self _bridge] nextKeyViewInsideWebFrameViews];
2943     }
2944     
2945     if (view == nil) {
2946         view = [super nextValidKeyView];
2947         // If there's no next view wired up, we must be in the last subframe, or we are
2948         // being called at an unusual time when the views have not yet been wired together.
2949         // There's no direct link to the next valid key view; get it from the bridge.
2950         // Note that view == self here when nextKeyView returns nil, due to AppKit oddness.
2951         // We'll check for both nil and self in case the AppKit oddness goes away.
2952         // WebFrameView has this same kind of logic for the previousValidKeyView case.
2953         if (view == nil || view == self) {
2954             view = [[self _bridge] nextValidKeyViewOutsideWebFrameViews];
2955         }
2956     }
2957         
2958     return view;
2959 }
2960
2961 - (NSView *)previousValidKeyView
2962 {
2963     NSView *view = nil;
2964     if (![self isHiddenOrHasHiddenAncestor])
2965         view = [[self _bridge] previousKeyViewInsideWebFrameViews];
2966     if (view == nil)
2967         view = [super previousValidKeyView];
2968     return view;
2969 }
2970
2971 - (BOOL)becomeFirstResponder
2972 {
2973     NSView *view = nil;
2974     if (![[self _webView] _isPerformingProgrammaticFocus] && !_private->willBecomeFirstResponderForNodeFocus) {
2975         switch ([[self window] keyViewSelectionDirection]) {
2976             case NSDirectSelection:
2977                 break;
2978             case NSSelectingNext:
2979                 view = [[self _bridge] nextKeyViewInsideWebFrameViews];
2980                 break;
2981             case NSSelectingPrevious:
2982                 view = [[self _bridge] previousKeyViewInsideWebFrameViews];
2983                 break;
2984         }
2985     }
2986     _private->willBecomeFirstResponderForNodeFocus = NO;
2987     if (view)
2988         [[self window] makeFirstResponder:view];
2989     [self _updateActiveState];
2990     [self _updateFontPanel];
2991     _private->startNewKillRingSequence = YES;
2992     return YES;
2993 }
2994
2995 - (BOOL)resignFirstResponder
2996 {
2997     BOOL resign = [super resignFirstResponder];
2998     if (resign) {
2999         [_private->compController endRevertingChange:NO moveLeft:NO];
3000         _private->resigningFirstResponder = YES;
3001         if (![self maintainsInactiveSelection]) { 
3002             if ([[self _webView] _isPerformingProgrammaticFocus])
3003                 [self deselectText];
3004             else
3005                 [self deselectAll];
3006         }
3007         [self _updateActiveState];
3008         _private->resigningFirstResponder = NO;
3009         _private->willBecomeFirstResponderForNodeFocus = NO;
3010     }
3011     return resign;
3012 }
3013
3014 - (void)setDataSource:(WebDataSource *)dataSource 
3015 {
3016     ASSERT(!_private->dataSource);
3017     _private->dataSource = [dataSource retain];
3018     [_private->pluginController setDataSource:dataSource];
3019     [self addMouseMovedObserver];
3020 }
3021
3022 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3023 {
3024 }
3025
3026 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3027 // key.  WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3028 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3029 - (void)updateCell:(NSCell*)cell
3030 {
3031 }
3032
3033 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3034 // pageWidth != 0 implies we will relayout to a new width
3035 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
3036 {
3037     WebFrame *frame = [self _frame];
3038     NSArray *subframes = [frame childFrames];
3039     unsigned n = [subframes count];
3040     unsigned i;
3041     for (i = 0; i != n; ++i) {
3042         WebFrame *subframe = [subframes objectAtIndex:i];
3043         WebFrameView *frameView = [subframe frameView];
3044         if ([[subframe dataSource] _isDocumentHTML]) {
3045             [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:adjustViewSize];
3046         }
3047     }
3048
3049     if (printing != _private->printing) {
3050         [_private->pageRects release];
3051         _private->pageRects = nil;
3052         _private->printing = printing;
3053         [self setNeedsToApplyStyles:YES];
3054         [self setNeedsLayout:YES];
3055         [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
3056         if (!printing) {
3057             // Can't do this when starting printing or nested printing won't work, see 3491427.
3058             [self setNeedsDisplay:NO];
3059         }
3060     }
3061 }
3062
3063 - (BOOL)canPrintHeadersAndFooters
3064 {
3065     return YES;
3066 }
3067
3068 // This is needed for the case where the webview is embedded in the view that's being printed.
3069 // It shouldn't be called when the webview is being printed directly.
3070 - (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit
3071 {
3072     // This helps when we print as part of a larger print process.
3073     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3074     BOOL wasInPrintingMode = _private->printing;
3075     if (!wasInPrintingMode)
3076         [self _setPrinting:YES minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
3077
3078     [[self _bridge] adjustPageHeightNew:newBottom top:oldTop bottom:oldBottom limit:bottomLimit];
3079     
3080     if (!wasInPrintingMode)
3081         [self _setPrinting:NO minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
3082 }
3083
3084 - (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
3085 {
3086     NSPrintInfo *printInfo = [printOperation printInfo];
3087     return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
3088 }
3089
3090 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3091 {
3092     float viewWidth = NSWidth([self bounds]);
3093     if (viewWidth < 1) {
3094         LOG_ERROR("%@ has no width when printing", self);
3095         return 1.0;
3096     }
3097
3098     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3099     float maxShrinkToFitScaleFactor = 1/PrintingMaximumShrinkFactor;
3100     float shrinkToFitScaleFactor = [self _availablePaperWidthForPrintOperation:printOperation]/viewWidth;
3101     return userScaleFactor * MAX(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor);
3102 }
3103
3104 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3105 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3106 // if AppKit makes it SPI/API.
3107 - (float)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation 
3108 {
3109     return [self _scaleFactorForPrintOperation:printOperation];
3110 }
3111
3112 // This is used for Carbon printing. At some point we might want to make this public API.
3113 - (void)setPageWidthForPrinting:(float)pageWidth
3114 {
3115     [self _setPrinting:NO minimumPageWidth:0. maximumPageWidth:0. adjustViewSize:NO];
3116     [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
3117 }
3118
3119 - (void)_endPrintMode
3120 {
3121     [self _setPrinting:NO minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:YES];
3122     [[self window] setAutodisplay:YES];
3123 }
3124
3125 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3126 {
3127     ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3128     NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3129     if (initiatingOperation == currentOperation) {
3130         // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3131         // being extra paranoid here since the printing code is so fragile. Delay the cleanup
3132         // further.
3133         ASSERT_NOT_REACHED();
3134         [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3135     } else if ([currentOperation view] == self) {
3136         // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3137         // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3138         // fragile. Do nothing, because we don't want to break the print job currently in progress, and
3139         // the print job currently in progress is responsible for its own cleanup.
3140         ASSERT_NOT_REACHED();
3141     } else {
3142         // The print job that kicked off this delayed call has finished, and this view is not being
3143         // printed again. We expect that no other print job has started. Since this delayed call wasn't
3144         // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3145         // the print mode here.
3146         ASSERT(currentOperation == nil);
3147         [self _endPrintMode];
3148     }
3149 }
3150
3151 // Return the number of pages available for printing
3152 - (BOOL)knowsPageRange:(NSRangePointer)range
3153 {
3154     // Must do this explicit display here, because otherwise the view might redisplay while the print
3155     // sheet was up, using printer fonts (and looking different).
3156     [self displayIfNeeded];
3157     [[self window] setAutodisplay:NO];
3158     
3159     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
3160     // according to the paper size
3161     float minLayoutWidth = 0.0;
3162     float maxLayoutWidth = 0.0;
3163     if (![[self _bridge] isFrameSet]) {
3164         float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
3165         minLayoutWidth = paperWidth*PrintingMinimumShrinkFactor;
3166         maxLayoutWidth = paperWidth*PrintingMaximumShrinkFactor;
3167     }
3168     [self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; // will relayout
3169     NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3170     // Certain types of errors, including invalid page ranges, can cause beginDocument and
3171     // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3172     // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3173     // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3174     // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3175     // which is after beginDocument and endDocument would be called.
3176     [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3177     [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3178     
3179     // There is a theoretical chance that someone could do some drawing between here and endDocument,
3180     // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3181     // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3182
3183     range->location = 1;
3184     float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3185     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3186     [_private->pageRects release];
3187     NSArray *newPageRects = [[self _bridge] computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3188                                                                           printHeight:floorf([self _calculatePrintHeight]/totalScaleFactor)];
3189     // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3190     // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3191     // a blank page (with correct-looking header and footer if that option is on), which matches
3192     // the behavior of IE and Camino at least.
3193     if ([newPageRects count] == 0)
3194         newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3195     _private->pageRects = [newPageRects retain];
3196     
3197     range->length = [_private->pageRects count];
3198     
3199     return YES;
3200 }
3201
3202 // Return the drawing rectangle for a particular page number
3203 - (NSRect)rectForPage:(int)page
3204 {
3205     return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3206 }
3207
3208 - (void)drawPageBorderWithSize:(NSSize)borderSize
3209 {
3210     ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));    
3211     [[self _webView] _drawHeaderAndFooter];
3212 }
3213
3214 - (void)beginDocument
3215 {
3216     NS_DURING
3217         // From now on we'll get a chance to call _endPrintMode in either beginDocument or
3218         // endDocument, so we can cancel the "just in case" pending call.
3219         [NSObject cancelPreviousPerformRequestsWithTarget:self
3220                                                  selector:@selector(_delayedEndPrintMode:)
3221                                                    object:[NSPrintOperation currentOperation]];
3222         [super beginDocument];
3223     NS_HANDLER
3224         // Exception during [super beginDocument] means that endDocument will not get called,
3225         // so we need to clean up our "print mode" here.
3226         [self _endPrintMode];
3227     NS_ENDHANDLER
3228 }
3229
3230 - (void)endDocument
3231 {
3232     [super endDocument];
3233     // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO 
3234     [self _endPrintMode];
3235 }
3236
3237 - (BOOL)_interceptEditingKeyEvent:(NSEvent *)event
3238 {   
3239     // Use WebView's tabKeyCyclesThroughElements state to determine whether or not
3240     // to process tab key events. The idea here is that tabKeyCyclesThroughElements
3241     // will be YES when this WebView is being used in a browser, and we desire the
3242     // behavior where tab moves to the next element in tab order. If tabKeyCyclesThroughElements
3243     // is NO, it is likely that the WebView is being embedded as the whole view, as in Mail,
3244     // and tabs should input tabs as expected in a text editor. Using Option-Tab always cycles
3245     // through elements.
3246
3247     if ([[self _webView] tabKeyCyclesThroughElements] && [event _web_isTabKeyEvent]) 
3248         return NO;
3249
3250     if (![[self _webView] tabKeyCyclesThroughElements] && [event _web_isOptionTabKeyEvent])
3251         return NO;
3252
3253     // Now process the key normally
3254     [self interpretKeyEvents:[NSArray arrayWithObject:event]];
3255     return YES;
3256 }
3257
3258 - (void)keyDown:(NSEvent *)event
3259 {
3260     [self retain];
3261
3262     BOOL callSuper = NO;
3263
3264     _private->keyDownEvent = event;
3265
3266     WebFrameBridge *bridge = [self _bridge];
3267     if ([bridge interceptKeyEvent:event toView:self]) {
3268         // WebCore processed a key event, bail on any outstanding complete: UI
3269         [_private->compController endRevertingChange:YES moveLeft:NO];
3270     } else if (_private->compController && [_private->compController filterKeyDown:event]) {
3271         // Consumed by complete: popup window
3272     } else {
3273         // We're going to process a key event, bail on any outstanding complete: UI
3274         [_private->compController endRevertingChange:YES moveLeft:NO];
3275         BOOL handledKey = [self _canEdit] && [self _interceptEditingKeyEvent:event];
3276         if (!handledKey)
3277             callSuper = YES;
3278     }
3279     if (callSuper)
3280         [super keyDown:event];
3281     else
3282         [NSCursor setHiddenUntilMouseMoves:YES];
3283
3284     _private->keyDownEvent = nil;
3285     
3286     [self release];
3287 }
3288
3289 - (void)keyUp:(NSEvent *)event
3290 {
3291     [self retain];
3292     if (![[self _bridge] interceptKeyEvent:event toView:self])
3293         [super keyUp:event];    
3294     [self release];
3295 }
3296
3297 - (id)accessibilityAttributeValue:(NSString*)attributeName
3298 {
3299     if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
3300         id accTree = [[self _bridge] accessibilityTree];
3301         if (accTree)
3302             return [NSArray arrayWithObject:accTree];
3303         return nil;
3304     }
3305     return [super accessibilityAttributeValue:attributeName];
3306 }
3307
3308 - (id)accessibilityFocusedUIElement
3309 {
3310     id accTree = [[self _bridge] accessibilityTree];
3311     if (accTree)
3312         return [accTree accessibilityFocusedUIElement];
3313     return self;
3314 }
3315
3316 - (id)accessibilityHitTest:(NSPoint)point
3317 {
3318     id accTree = [[self _bridge] accessibilityTree];
3319     if (accTree) {
3320         NSPoint windowCoord = [[self window] convertScreenToBase:point];
3321         return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
3322     }
3323     return self;
3324 }
3325
3326 - (id)_accessibilityParentForSubview:(NSView *)subview
3327 {
3328     id accTree = [[self _bridge] accessibilityTree];
3329     if (!accTree)
3330         return self;
3331     id parent = [accTree _accessibilityParentForSubview:subview];
3332     if (!parent)
3333         return self;
3334     return parent;
3335 }
3336
3337 - (void)centerSelectionInVisibleArea:(id)sender
3338 {
3339     [[self _bridge] centerSelectionInVisibleArea];
3340 }
3341
3342 - (void)_alterCurrentSelection:(WebSelectionAlteration)alteration direction:(WebBridgeSelectionDirection)direction granularity:(WebBridgeSelectionGranularity)granularity
3343 {
3344     if (![self _canAlterCurrentSelection])
3345         return;
3346         
3347     WebFrameBridge *bridge = [self _bridge];
3348     DOMRange *proposedRange = [bridge rangeByAlteringCurrentSelection:alteration direction:direction granularity:granularity];
3349     WebView *webView = [self _webView];
3350     if ([[webView _editingDelegateForwarder] webView:webView shouldChangeSelectedDOMRange:[self _selectedRange] toDOMRange:proposedRange affinity:[bridge selectionAffinity] stillSelecting:NO]) {
3351         [bridge alterCurrentSelection:alteration direction:direction granularity:granularity];
3352     }
3353 }
3354
3355 - (void)_alterCurrentSelection:(WebSelectionAlteration)alteration verticalDistance:(float)verticalDistance
3356 {
3357     if (![self _canAlterCurrentSelection])
3358         return;
3359         
3360     WebFrameBridge *bridge = [self _bridge];
3361     DOMRange *proposedRange = [bridge rangeByAlteringCurrentSelection:alteration verticalDistance:verticalDistance];
3362     WebView *webView = [self _webView];
3363     if ([[webView _editingDelegateForwarder] webView:webView shouldChangeSelectedDOMRange:[self _selectedRange] toDOMRange:proposedRange affinity:[bridge selectionAffinity] stillSelecting:NO]) {
3364         [bridge alterCurrentSelection:alteration verticalDistance:verticalDistance];
3365     }
3366 }
3367
3368 - (void)moveBackward:(id)sender
3369 {
3370     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectByCharacter];
3371 }
3372
3373 - (void)moveBackwardAndModifySelection:(id)sender
3374 {
3375     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectByCharacter];
3376 }
3377
3378 - (void)moveDown:(id)sender
3379 {
3380     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectByLine];
3381 }
3382
3383 - (void)moveDownAndModifySelection:(id)sender
3384 {
3385     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectByLine];
3386 }
3387
3388 - (void)moveForward:(id)sender
3389 {
3390     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectByCharacter];
3391 }
3392
3393 - (void)moveForwardAndModifySelection:(id)sender
3394 {
3395     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectByCharacter];
3396 }
3397
3398 - (void)moveLeft:(id)sender
3399 {
3400     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectLeft granularity:WebBridgeSelectByCharacter];
3401 }
3402
3403 - (void)moveLeftAndModifySelection:(id)sender
3404 {
3405     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectLeft granularity:WebBridgeSelectByCharacter];
3406 }
3407
3408 - (void)moveRight:(id)sender
3409 {
3410     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectRight granularity:WebBridgeSelectByCharacter];
3411 }
3412
3413 - (void)moveRightAndModifySelection:(id)sender
3414 {
3415     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectRight granularity:WebBridgeSelectByCharacter];
3416 }
3417
3418 - (void)moveToBeginningOfDocument:(id)sender
3419 {
3420     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToDocumentBoundary];
3421 }
3422
3423 - (void)moveToBeginningOfDocumentAndModifySelection:(id)sender
3424 {
3425     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToDocumentBoundary];
3426 }
3427
3428 - (void)moveToBeginningOfSentence:(id)sender
3429 {
3430     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToSentenceBoundary];
3431 }
3432
3433 - (void)moveToBeginningOfSentenceAndModifySelection:(id)sender
3434 {
3435     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToSentenceBoundary];
3436 }
3437
3438 - (void)moveToBeginningOfLine:(id)sender
3439 {
3440     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToLineBoundary];
3441 }
3442
3443 - (void)moveToBeginningOfLineAndModifySelection:(id)sender
3444 {
3445     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToLineBoundary];
3446 }
3447
3448 - (void)moveToBeginningOfParagraph:(id)sender
3449 {
3450     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToParagraphBoundary];
3451 }
3452
3453 - (void)moveToBeginningOfParagraphAndModifySelection:(id)sender
3454 {
3455     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToParagraphBoundary];
3456 }
3457
3458 - (void)moveToEndOfDocument:(id)sender
3459 {
3460     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectToDocumentBoundary];
3461 }
3462
3463 - (void)moveToEndOfDocumentAndModifySelection:(id)sender
3464 {
3465     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectToDocumentBoundary];
3466 }
3467
3468 - (void)moveToEndOfSentence:(id)sender
3469 {
3470     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectToSentenceBoundary];
3471 }
3472
3473 - (void)moveToEndOfSentenceAndModifySelection:(id)sender
3474 {
3475     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectToSentenceBoundary];
3476 }
3477
3478 - (void)moveToEndOfLine:(id)sender
3479 {
3480     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectToLineBoundary];
3481 }
3482
3483 - (void)moveToEndOfLineAndModifySelection:(id)sender
3484 {
3485     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectToLineBoundary];
3486 }
3487
3488 - (void)moveToEndOfParagraph:(id)sender
3489 {
3490     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectToParagraphBoundary];
3491 }
3492
3493 - (void)moveToEndOfParagraphAndModifySelection:(id)sender
3494 {
3495     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectToParagraphBoundary];
3496 }
3497
3498 - (void)moveParagraphBackwardAndModifySelection:(id)sender
3499 {
3500     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectByParagraph];
3501 }
3502
3503 - (void)moveParagraphForwardAndModifySelection:(id)sender
3504 {
3505     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectByParagraph];
3506 }
3507
3508 - (void)moveUp:(id)sender
3509 {
3510     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectByLine];
3511 }
3512
3513 - (void)moveUpAndModifySelection:(id)sender
3514 {
3515     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectByLine];
3516 }
3517
3518 - (void)moveWordBackward:(id)sender
3519 {
3520     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectByWord];
3521 }
3522
3523 - (void)moveWordBackwardAndModifySelection:(id)sender
3524 {
3525     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectByWord];
3526 }
3527
3528 - (void)moveWordForward:(id)sender
3529 {
3530     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectByWord];
3531 }
3532
3533 - (void)moveWordForwardAndModifySelection:(id)sender
3534 {
3535     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectByWord];
3536 }
3537
3538 - (void)moveWordLeft:(id)sender
3539 {
3540     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectLeft granularity:WebBridgeSelectByWord];
3541 }
3542
3543 - (void)moveWordLeftAndModifySelection:(id)sender
3544 {
3545     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectLeft granularity:WebBridgeSelectByWord];
3546 }
3547
3548 - (void)moveWordRight:(id)sender
3549 {
3550     [self _alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectRight granularity:WebBridgeSelectByWord];
3551 }
3552
3553 - (void)moveWordRightAndModifySelection:(id)sender
3554 {
3555     [self _alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectRight granularity:WebBridgeSelectByWord];
3556 }
3557
3558 - (void)pageUp:(id)sender
3559 {
3560     WebFrameView *frameView = [self _frameView];
3561     if (frameView == nil)
3562         return;
3563     [self _alterCurrentSelection:WebSelectByMoving verticalDistance:-[frameView _verticalPageScrollDistance]];
3564 }
3565
3566 - (void)pageDown:(id)sender
3567 {
3568     WebFrameView *frameView = [self _frameView];
3569     if (frameView == nil)
3570         return;
3571     [self _alterCurrentSelection:WebSelectByMoving verticalDistance:[frameView _verticalPageScrollDistance]];
3572 }
3573
3574 - (void)pageUpAndModifySelection:(id)sender
3575 {
3576     WebFrameView *frameView = [self _frameView];
3577     if (frameView == nil)
3578         return;
3579     [self _alterCurrentSelection:WebSelectByExtending verticalDistance:-[frameView _verticalPageScrollDistance]];
3580 }
3581
3582 - (void)pageDownAndModifySelection:(id)sender
3583 {
3584     WebFrameView *frameView = [self _frameView];
3585     if (frameView == nil)
3586         return;
3587     [self _alterCurrentSelection:WebSelectByExtending verticalDistance:[frameView _verticalPageScrollDistance]];
3588 }
3589
3590 - (void)_expandSelectionToGranularity:(WebBridgeSelectionGranularity)granularity
3591 {
3592     if (![self _canAlterCurrentSelection])
3593         return;
3594         
3595     WebFrameBridge *bridge = [self _bridge];
3596     DOMRange *range = [bridge rangeByExpandingSelectionWithGranularity:granularity];
3597     if (range && ![range collapsed]) {
3598         WebView *webView = [self _webView];
3599         if ([[webView _editingDelegateForwarder] webView:webView shouldChangeSelectedDOMRange:[self _selectedRange] toDOMRange:range affinity:[bridge selectionAffinity] stillSelecting:NO])
3600             [bridge setSelectedDOMRange:range affinity:[bridge selectionAffinity] closeTyping:YES];
3601     }
3602 }
3603
3604 - (void)selectParagraph:(id)sender
3605 {
3606     [self _expandSelectionToGranularity:WebBridgeSelectByParagraph];
3607 }
3608
3609 - (void)selectLine:(id)sender
3610 {
3611     [self _expandSelectionToGranularity:WebBridgeSelectByLine];
3612 }
3613
3614 - (void)selectSentence:(id)sender
3615 {
3616     [self _expandSelectionToGranularity:WebBridgeSelectBySentence];
3617 }
3618
3619 - (void)selectWord:(id)sender
3620 {
3621     [self _expandSelectionToGranularity:WebBridgeSelectByWord];
3622 }
3623
3624 - (void)copy:(id)sender
3625 {
3626     if ([[self _bridge] tryDHTMLCopy])
3627         return; // DHTML did the whole operation
3628     if (![self _canCopy]) {
3629         NSBeep();
3630         return;
3631     }
3632     [self _writeSelectionToPasteboard:[NSPasteboard generalPasteboard]];
3633 }
3634
3635 - (void)delete:(id)sender
3636 {
3637     if (![self _canDelete]) {
3638         NSBeep();
3639         return;
3640     }
3641     [self _deleteSelection];
3642 }
3643
3644 - (void)cut:(id)sender
3645 {
3646     WebFrameBridge *bridge = [self _bridge];
3647     if ([bridge tryDHTMLCut])
3648         return; // DHTML did the whole operation
3649     if (![self _canCut]) {
3650         NSBeep();
3651         return;
3652     }
3653     DOMRange *range = [self _selectedRange];
3654     if ([self _shouldDeleteRange:range]) {
3655         [self _writeSelectionToPasteboard:[NSPasteboard generalPasteboard]];
3656         [bridge deleteSelectionWithSmartDelete:[self _canSmartCopyOrDelete]];
3657    }
3658 }
3659
3660 - (void)paste:(id)sender
3661 {
3662     if ([[self _bridge] tryDHTMLPaste])
3663         return;     // DHTML did the whole operation
3664     if (![self _canPaste])
3665         return;
3666     if ([[self _bridge] isSelectionRichlyEditable])
3667         [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:YES];
3668     else
3669         [self _pasteAsPlainTextWithPasteboard:[NSPasteboard generalPasteboard]];
3670 }
3671
3672 - (NSData *)_selectionStartFontAttributesAsRTF
3673 {
3674     NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x"
3675         attributes:[[self _bridge] fontAttributesForSelectionStart]];
3676     NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
3677     [string release];
3678     return data;
3679 }
3680
3681 - (NSDictionary *)_fontAttributesFromFontPasteboard
3682 {
3683     NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
3684     if (fontPasteboard == nil)
3685         return nil;
3686     NSData *data = [fontPasteboard dataForType:NSFontPboardType];
3687     if (data == nil || [data length] == 0)
3688         return nil;
3689     // NSTextView does something more efficient by parsing the attributes only, but that's not available in API.
3690     NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease];
3691     if (string == nil || [string length] == 0)
3692         return nil;
3693     return [string fontAttributesInRange:NSMakeRange(0, 1)];
3694 }
3695
3696 - (DOMCSSStyleDeclaration *)_emptyStyle
3697 {
3698     return [[[self _bridge] DOMDocument] createCSSStyleDeclaration];
3699 }
3700
3701 - (NSString *)_colorAsString:(NSColor *)color
3702 {
3703     NSColor *rgbColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
3704     // FIXME: If color is non-nil and rgbColor is nil, that means we got some kind
3705     // of fancy color that can't be converted to RGB. Changing that to "transparent"
3706     // might not be great, but it's probably OK.
3707     if (rgbColor == nil)
3708         return @"transparent";
3709     float r = [rgbColor redComponent];
3710     float g = [rgbColor greenComponent];
3711     float b = [rgbColor blueComponent];
3712     float a = [rgbColor alphaComponent];
3713     if (a == 0)
3714         return @"transparent";
3715     if (r == 0 && g == 0 && b == 0 && a == 1)
3716         return @"black";
3717     if (r == 1 && g == 1 && b == 1 && a == 1)
3718         return @"white";
3719     // FIXME: Lots more named colors. Maybe we could use the table in WebCore?
3720     if (a == 1)
3721         return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255];
3722     return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a];
3723 }
3724
3725 - (NSString *)_shadowAsString:(NSShadow *)shadow
3726 {
3727     if (shadow == nil)
3728         return @"none";
3729     NSSize offset = [shadow shadowOffset];
3730     float blurRadius = [shadow shadowBlurRadius];
3731     if (offset.width == 0 && offset.height == 0 && blurRadius == 0)
3732         return @"none";
3733     NSColor *color = [shadow shadowColor];
3734     if (color == nil)
3735         return @"none";
3736     // FIXME: Handle non-integral values here?
3737     if (blurRadius == 0)
3738         return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height];
3739     return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius];
3740 }
3741
3742 - (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary
3743 {
3744     DOMCSSStyleDeclaration *style = [self _emptyStyle];
3745
3746     NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName];
3747     [style setBackgroundColor:[self _colorAsString:color]];
3748
3749     NSFont *font = [dictionary objectForKey:NSFontAttributeName];
3750     if (font == nil) {
3751         [style setFontFamily:@"Helvetica"];
3752         [style setFontSize:@"12px"];
3753         [style setFontWeight:@"normal"];
3754         [style setFontStyle:@"normal"];
3755     } else {
3756         NSFontManager *fm = [NSFontManager sharedFontManager];
3757         // FIXME: Need more sophisticated escaping code if we want to handle family names
3758         // with characters like single quote or backslash in their names.
3759         [style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]];
3760         [style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]];
3761         if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT)
3762             [style setFontWeight:@"bold"];
3763         else
3764             [style setFontWeight:@"normal"];
3765         if (([fm traitsOfFont:font] & NSItalicFontMask) != 0)
3766             [style setFontStyle:@"italic"];
3767         else
3768             [style setFontStyle:@"normal"];
3769     }
3770
3771     color = [dictionary objectForKey:NSForegroundColorAttributeName];
3772     [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"];
3773
3774     NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName];
3775     [style setTextShadow:[self _shadowAsString:shadow]];
3776
3777     int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue];
3778
3779     int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue];
3780     if (superscriptInt > 0)
3781         [style setVerticalAlign:@"super"];
3782     else if (superscriptInt < 0)
3783         [style setVerticalAlign:@"sub"];
3784     else
3785         [style setVerticalAlign:@"baseline"];
3786     int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue];
3787     // FIXME: Underline wins here if we have both (see bug 3790443).
3788     if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone)
3789         [style setProperty:@"-khtml-text-decorations-in-effect" :@"none" :@""];
3790     else if (underlineInt == NSUnderlineStyleNone)
3791         [style setProperty:@"-khtml-text-decorations-in-effect" :@"line-through" :@""];
3792     else
3793         [style setProperty:@"-khtml-text-decorations-in-effect" :@"underline" :@""];
3794
3795     return style;
3796 }
3797
3798 - (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(WebUndoAction)undoAction
3799 {
3800     if (style == nil || [style length] == 0 || ![self _canEditRichly])
3801         return;
3802     WebView *webView = [self _webView];
3803     WebFrameBridge *bridge = [self _bridge];
3804     if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:[self _selectedRange]]) {
3805         [bridge applyStyle:style withUndoAction:undoAction];
3806     }
3807 }
3808
3809 - (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(WebUndoAction)undoAction
3810 {
3811     if (style == nil || [style length] == 0 || ![self _canEditRichly])
3812         return;
3813     WebView *webView = [self _webView];
3814     WebFrameBridge *bridge = [self _bridge];
3815     if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:[self _selectedRange]])
3816         [bridge applyParagraphStyle:style withUndoAction:undoAction];
3817 }
3818
3819 - (void)_toggleBold
3820 {
3821     DOMCSSStyleDeclaration *style = [self _emptyStyle];
3822     [style setFontWeight:@"bold"];
3823     if ([[self _bridge] selectionStartHasStyle:style])
3824         [style setFontWeight:@"normal"];
3825     [self _applyStyleToSelection:style withUndoAction:WebUndoActionSetFont];
3826 }
3827
3828 - (void)_toggleItalic
3829 {
3830     DOMCSSStyleDeclaration *style = [self _emptyStyle];
3831     [style setFontStyle:@"italic"];
3832     if ([[self _bridge] selectionStartHasStyle:style])
3833         [style setFontStyle:@"normal"];
3834     [self _applyStyleToSelection:style withUndoAction:WebUndoActionSetFont];
3835 }
3836
3837 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
3838 {
3839     ASSERT([self _webView]);
3840     if (![[[self _webView] preferences] respectStandardStyleKeyEquivalents])
3841         return NO;
3842     
3843     if (![self _canEdit])
3844         return NO;
3845     
3846     NSString *string = [event charactersIgnoringModifiers];
3847     if ([string isEqualToString:@"b"]) {
3848         [self _toggleBold];
3849         return YES;
3850     }
3851     if ([string isEqualToString:@"i"]) {
3852         [self _toggleItalic];
3853         return YES;
3854     }
3855     
3856     return NO;
3857 }
3858
3859 - (BOOL)performKeyEquivalent:(NSEvent *)event
3860 {
3861     if ([self _handleStyleKeyEquivalent:event])
3862         return YES;
3863     
3864     BOOL ret;
3865     
3866     [self retain];
3867     
3868     // Pass command-key combos through WebCore if there is a key binding available for
3869     // this event. This lets web pages have a crack at intercepting command-modified keypresses.
3870     // But don't do it if we have already handled the event.
3871     if (event != _private->keyDownEvent
3872             && [self _web_firstResponderIsSelfOrDescendantView]
3873             && [[self _bridge] interceptKeyEvent:event toView:self])
3874         ret = YES;
3875     else
3876         ret = [super performKeyEquivalent:event];
3877     
3878     [self release];
3879     
3880     return ret;
3881 }
3882
3883 - (void)copyFont:(id)sender
3884 {
3885     // Put RTF with font attributes on the pasteboard.
3886     // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
3887     NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
3888     [fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil];
3889     [fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType];
3890 }
3891
3892 - (void)pasteFont:(id)sender
3893 {
3894     // Read RTF with font attributes from the pasteboard.
3895     // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
3896     [self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:WebUndoActionPasteFont];
3897 }
3898
3899 - (void)pasteAsPlainText:(id)sender
3900 {
3901     if (![self _canEdit])
3902         return;
3903     [self _pasteAsPlainTextWithPasteboard:[NSPasteboard generalPasteboard]];
3904 }
3905
3906 - (void)pasteAsRichText:(id)sender
3907 {
3908     // Since rich text always beats plain text when both are on the pasteboard, it's not
3909     // clear how this is different from plain old paste.
3910     [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO];
3911 }
3912
3913 - (NSFont *)_originalFontA
3914 {
3915     return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10];
3916 }
3917
3918 - (NSFont *)_originalFontB
3919 {
3920     return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:(NSBoldFontMask | NSItalicFontMask) weight:STANDARD_BOLD_WEIGHT size:12];
3921 }
3922
3923 - (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b
3924 {
3925     // Since there's no way to directly ask NSFontManager what style change it's going to do
3926     // we instead pass two "specimen" fonts to it and let it change them. We then deduce what
3927     // style change it was doing by looking at what happened to each of the two fonts.
3928     // So if it was making the text bold, both fonts will be bold after the fact.
3929
3930     if (a == nil || b == nil)
3931         return;
3932
3933     NSFontManager *fm = [NSFontManager sharedFontManager];
3934
3935     NSFont *oa = [self _originalFontA];
3936
3937     NSString *aFamilyName = [a familyName];
3938     NSString *bFamilyName = [b familyName];
3939
3940     int aPointSize = [a pointSize];
3941     int bPointSize = [b pointSize];
3942
3943     int aWeight = [fm weightOfFont:a];
3944     int bWeight = [fm weightOfFont:b];
3945
3946     BOOL aIsBold = aWeight >= MIN_BOLD_WEIGHT;
3947
3948     BOOL aIsItalic = ([fm traitsOfFont:a] & NSItalicFontMask) != 0;
3949     BOOL bIsItalic = ([fm traitsOfFont:b] & NSItalicFontMask) != 0;
3950
3951     if ([aFamilyName isEqualToString:bFamilyName]) {
3952         NSString *familyNameForCSS = aFamilyName;
3953
3954         // The family name may not be specific enough to get us the font specified.
3955         // In some cases, the only way to get exactly what we are looking for is to use
3956         // the Postscript name.
3957         
3958         // Find the font the same way the rendering code would later if it encountered this CSS.
3959         NSFontTraitMask traits = 0;
3960         if (aIsBold)
3961             traits |= NSBoldFontMask;
3962         if (aIsItalic)
3963             traits |= NSItalicFontMask;
3964         NSFont *foundFont = WebCoreFindFont(aFamilyName, traits, aPointSize);
3965
3966         // If we don't find a font with the same Postscript name, then we'll have to use the
3967         // Postscript name to make the CSS specific enough.
3968         if (![[foundFont fontName] isEqualToString:[a fontName]]) {
3969             familyNameForCSS = [a fontName];
3970         }
3971
3972         // FIXME: Need more sophisticated escaping code if we want to handle family names
3973         // with characters like single quote or backslash in their names.
3974         [style setFontFamily:[NSString stringWithFormat:@"'%@'", familyNameForCSS]];
3975     }
3976
3977     int soa = [oa pointSize];
3978     if (aPointSize == bPointSize)
3979         [style setFontSize:[NSString stringWithFormat:@"%dpx", aPointSize]];
3980     else if (aPointSize < soa)
3981         [style _setFontSizeDelta:@"-1px"];
3982     else if (aPointSize > soa)
3983         [style _setFontSizeDelta:@"1px"];
3984
3985     if (aWeight == bWeight)
3986         [style setFontWeight:aIsBold ? @"bold" : @"normal"];
3987
3988     if (aIsItalic == bIsItalic)
3989         [style setFontStyle:aIsItalic ? @"italic" :  @"normal"];
3990 }
3991
3992 - (DOMCSSStyleDeclaration *)_styleFromFontManagerOperation
3993 {
3994     DOMCSSStyleDeclaration *style = [self _emptyStyle];
3995
3996     NSFontManager *fm = [NSFontManager sharedFontManager];
3997
3998     NSFont *oa = [self _originalFontA];
3999     NSFont *ob = [self _originalFontB];    
4000     [self _addToStyle:style fontA:[fm convertFont:oa] fontB:[fm convertFont:ob]];
4001
4002     return style;
4003 }
4004
4005 - (void)changeFont:(id)sender
4006 {
4007     [self _applyStyleToSelection:[self _styleFromFontManagerOperation] withUndoAction:WebUndoActionSetFont];
4008 }
4009
4010 - (DOMCSSStyleDeclaration *)_styleForAttributeChange:(id)sender
4011 {
4012     DOMCSSStyleDeclaration *style = [self _emptyStyle];
4013
4014     NSShadow *shadow = [[NSShadow alloc] init];
4015     [shadow setShadowOffset:NSMakeSize(1, 1)];
4016
4017     NSDictionary *oa = [NSDictionary dictionaryWithObjectsAndKeys:
4018         [self _originalFontA], NSFontAttributeName,
4019         nil];
4020     NSDictionary *ob = [NSDictionary dictionaryWithObjectsAndKeys:
4021         [NSColor blackColor], NSBackgroundColorAttributeName,
4022         [self _originalFontB], NSFontAttributeName,
4023         [NSColor whiteColor], NSForegroundColorAttributeName,
4024         shadow, NSShadowAttributeName,
4025         [NSNumber numberWithInt:NSUnderlineStyleSingle], NSStrikethroughStyleAttributeName,
4026         [NSNumber numberWithInt:1], NSSuperscriptAttributeName,
4027         [NSNumber numberWithInt:NSUnderlineStyleSingle], NSUnderlineStyleAttributeName,
4028         nil];
4029
4030     [shadow release];
4031
4032 #if 0
4033
4034 NSObliquenessAttributeName        /* float; skew to be applied to glyphs, default 0: no skew */
4035     // font-style, but that is just an on-off switch
4036
4037 NSExpansionAttributeName          /* float; log of expansion factor to be applied to glyphs, default 0: no expansion */
4038     // font-stretch?
4039
4040 NSKernAttributeName               /* float, amount to modify default kerning, if 0, kerning off */
4041     // letter-spacing? probably not good enough
4042
4043 NSUnderlineColorAttributeName     /* NSColor, default nil: same as foreground color */
4044 NSStrikethroughColorAttributeName /* NSColor, default nil: same as foreground color */
4045     // text-decoration-color?
4046
4047 NSLigatureAttributeName           /* int, default 1: default ligatures, 0: no ligatures, 2: all ligatures */
4048 NSBaselineOffsetAttributeName     /* float, in points; offset from baseline, default 0 */
4049 NSStrokeWidthAttributeName        /* float, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0) */
4050 NSStrokeColorAttributeName        /* NSColor, default nil: same as foreground color */
4051     // need extensions?
4052
4053 #endif
4054     
4055     NSDictionary *a = [sender convertAttributes:oa];
4056     NSDictionary *b = [sender convertAttributes:ob];
4057
4058     NSColor *ca = [a objectForKey:NSBackgroundColorAttributeName];
4059     NSColor *cb = [b objectForKey:NSBackgroundColorAttributeName];
4060     if (ca == cb) {
4061         [style setBackgroundColor:[self _colorAsString:ca]];
4062     }
4063
4064     [self _addToStyle:style fontA:[a objectForKey:NSFontAttributeName] fontB:[b objectForKey:NSFontAttributeName]];
4065
4066     ca = [a objectForKey:NSForegroundColorAttributeName];
4067     cb = [b objectForKey:NSForegroundColorAttributeName];
4068     if (ca == cb) {
4069         [style setColor:[self _colorAsString:ca]];
4070     }
4071
4072     NSShadow *sha = [a objectForKey:NSShadowAttributeName];
4073     if (sha)
4074         [style setTextShadow:[self _shadowAsString:sha]];
4075     else if ([b objectForKey:NSShadowAttributeName] == nil)
4076         [style setTextShadow:@"none"];
4077
4078     int sa = [[a objectForKey:NSStrikethroughStyleAttributeName] intValue];
4079     int sb = [[b objectForKey:NSStrikethroughStyleAttributeName] intValue];
4080     if (sa == sb) {
4081         if (sa == NSUnderlineStyleNone)
4082             [style setProperty:@"-khtml-text-decorations-in-effect" :@"none" :@""]; 
4083             // we really mean "no line-through" rather than "none"
4084         else
4085             [style setProperty:@"-khtml-text-decorations-in-effect" :@"line-through" :@""];
4086             // we really mean "add line-through" rather than "line-through"
4087     }
4088
4089     sa = [[a objectForKey:NSSuperscriptAttributeName] intValue];
4090     sb = [[b objectForKey:NSSuperscriptAttributeName] intValue];
4091     if (sa == sb) {
4092         if (sa > 0)
4093             [style setVerticalAlign:@"super"];
4094         else if (sa < 0)
4095             [style setVerticalAlign:@"sub"];
4096         else
4097             [style setVerticalAlign:@"baseline"];
4098     }
4099
4100     int ua = [[a objectForKey:NSUnderlineStyleAttributeName] intValue];
4101     int ub = [[b objectForKey:NSUnderlineStyleAttributeName] intValue];
4102     if (ua == ub) {
4103         if (ua == NSUnderlineStyleNone)
4104             [style setProperty:@"-khtml-text-decorations-in-effect" :@"none" :@""];
4105             // we really mean "no underline" rather than "none"
4106         else
4107             [style setProperty:@"-khtml-text-decorations-in-effect" :@"underline" :@""];
4108             // we really mean "add underline" rather than "underline"
4109     }
4110
4111     return style;
4112 }
4113
4114 - (void)changeAttributes:(id)sender
4115 {
4116     [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:WebUndoActionChangeAttributes];
4117 }
4118
4119 - (DOMCSSStyleDeclaration *)_styleFromColorPanelWithSelector:(SEL)selector
4120 {
4121     DOMCSSStyleDeclaration *style = [self _emptyStyle];
4122
4123     ASSERT([style respondsToSelector:selector]);
4124     [style performSelector:selector withObject:[self _colorAsString:[[NSColorPanel sharedColorPanel] color]]];
4125     
4126     return style;
4127 }
4128
4129 - (WebUndoAction)_undoActionFromColorPanelWithSelector:(SEL)selector
4130 {
4131     if (selector == @selector(setBackgroundColor:))
4132         return WebUndoActionSetBackgroundColor;    
4133     return WebUndoActionSetColor;
4134 }
4135
4136 - (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range
4137 {
4138     DOMCSSStyleDeclaration *style = [self _styleFromColorPanelWithSelector:selector];
4139     WebView *webView = [self _webView];
4140     if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:range])
4141         [[self _bridge] applyStyle:style withUndoAction:[self _undoActionFromColorPanelWithSelector:selector]];
4142 }
4143
4144 - (void)changeDocumentBackgroundColor:(id)sender
4145 {
4146     // Mimicking NSTextView, this method sets the background color for the
4147     // entire document. There is no NSTextView API for setting the background
4148     // color on the selected range only. Note that this method is currently
4149     // never called from the UI (see comment in changeColor:).
4150     // FIXME: this actually has no effect when called, probably due to 3654850. _documentRange seems
4151     // to do the right thing because it works in startSpeaking:, and I know setBackgroundColor: does the
4152     // right thing because I tested it with [self _selectedRange].
4153     // FIXME: This won't actually apply the style to the entire range here, because it ends up calling
4154     // [bridge applyStyle:], which operates on the current selection. To make this work right, we'll
4155     // need to save off the selection, temporarily set it to the entire range, make the change, then
4156     // restore the old selection.
4157     [self _changeCSSColorUsingSelector:@selector(setBackgroundColor:) inRange:[self _documentRange]];