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