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