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