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