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