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