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