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