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