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