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