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