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