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