9497e0c51b58ce0ad888affc89982b514aa8290f
[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
142 @interface WebMenuTarget : NSObject {
143     WebCore::ContextMenuController* _menuController;
144 }
145 + (WebMenuTarget*)sharedMenuTarget;
146 - (WebCore::ContextMenuController*)menuController;
147 - (void)setMenuController:(WebCore::ContextMenuController*)menuController;
148 - (void)forwardContextMenuAction:(id)sender;
149 - (BOOL)validateMenuItem:(NSMenuItem *)item;
150 @end
151
152 static WebMenuTarget* target;
153
154 @implementation WebMenuTarget
155
156 + (WebMenuTarget*)sharedMenuTarget
157 {
158     if (!target)
159         target = [[WebMenuTarget alloc] init];
160     return target;
161 }
162
163 - (WebCore::ContextMenuController*)menuController
164 {
165     return _menuController;
166 }
167
168 - (void)setMenuController:(WebCore::ContextMenuController*)menuController
169 {
170     _menuController = menuController;
171 }
172
173 - (void)forwardContextMenuAction:(id)sender
174 {
175     WebCore::ContextMenuItem item(WebCore::ActionType, static_cast<WebCore::ContextMenuAction>([sender tag]), [sender title]);
176     _menuController->contextMenuItemSelected(&item);
177 }
178
179 - (BOOL)validateMenuItem:(NSMenuItem *)item
180 {
181     WebCore::ContextMenuItem coreItem(item);
182     ASSERT(_menuController->contextMenu());
183     _menuController->checkOrEnableIfNeeded(coreItem);
184     return coreItem.enabled();
185 }
186
187 @end
188
189 @interface NSWindow (BorderViewAccess)
190 - (NSView*)_web_borderView;
191 @end
192
193 @implementation NSWindow (BorderViewAccess)
194 - (NSView*)_web_borderView
195 {
196     return _borderView;
197 }
198 @end
199
200 @interface WebResponderChainSink : NSResponder {
201     NSResponder* _lastResponderInChain;
202     BOOL _receivedUnhandledCommand;
203 }
204 - (id)initWithResponderChain:(NSResponder *)chain;
205 - (void)detach;
206 - (BOOL)receivedUnhandledCommand;
207 @end
208
209 @interface WebLayerHostingFlippedView : NSView
210 @end
211
212 @implementation WebLayerHostingFlippedView
213 - (BOOL)isFlipped
214 {
215     return YES;
216 }
217 @end
218
219 @interface WebRootLayer : CALayer
220 @end
221
222 @implementation WebRootLayer
223 - (void)renderInContext:(CGContextRef)ctx
224 {
225     // AppKit calls -[CALayer renderInContext:] to render layer-backed views
226     // into bitmap contexts, but renderInContext: doesn't capture mask layers
227     // (<rdar://problem/9539526>), so we can't rely on it. Since our layer
228     // contents will have already been rendered by drawRect:, we can safely make
229     // this a NOOP.
230 }
231 @end
232
233 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
234 static BOOL forceNSViewHitTest;
235
236 // 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])
237 static BOOL forceWebHTMLViewHitTest;
238
239 static WebHTMLView *lastHitView;
240
241 static bool needsCursorRectsSupportAtPoint(NSWindow* window, NSPoint point)
242 {
243     forceNSViewHitTest = YES;
244     NSView* view = [[window _web_borderView] hitTest:point];
245     forceNSViewHitTest = NO;
246
247     // WebHTMLView doesn't use cursor rects.
248     if ([view isKindOfClass:[WebHTMLView class]])
249         return false;
250
251 #if ENABLE(NETSCAPE_PLUGIN_API)
252     // Neither do NPAPI plug-ins.
253     if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
254         return false;
255 #endif
256
257     // Non-Web content, WebPDFView, and WebKit plug-ins use normal cursor handling.
258     return true;
259 }
260
261
262 static IMP oldSetCursorForMouseLocationIMP;
263
264 // Overriding an internal method is a hack; <rdar://problem/7662987> tracks finding a better solution.
265 static void setCursor(NSWindow *self, SEL cmd, NSPoint point)
266 {
267     if (needsCursorRectsSupportAtPoint(self, point))
268         wtfCallIMP<id>(oldSetCursorForMouseLocationIMP, self, cmd, point);
269 }
270
271
272 extern "C" {
273
274 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
275
276 extern NSString *NSMarkedClauseSegmentAttributeName;
277 extern NSString *NSTextInputReplacementRangeAttributeName;
278
279 }
280
281 @interface NSView (WebNSViewDetails)
282 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
283 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
284 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView;
285 - (void)_recursive:(BOOL)recurseX displayRectIgnoringOpacity:(NSRect)displayRect inGraphicsContext:(NSGraphicsContext *)graphicsContext CGContext:(CGContextRef)ctx topView:(BOOL)isTopView shouldChangeFontReferenceColor:(BOOL)shouldChangeFontReferenceColor;
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     [self _setAsideSubviews];
1362     [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView];
1363     [self _restoreSubviews];
1364 }
1365
1366 // Don't let AppKit even draw subviews. We take care of that.
1367 - (void)_recursive:(BOOL)recurseX displayRectIgnoringOpacity:(NSRect)displayRect inGraphicsContext:(NSGraphicsContext *)graphicsContext CGContext:(CGContextRef)ctx topView:(BOOL)isTopView shouldChangeFontReferenceColor:(BOOL)shouldChangeFontReferenceColor
1368 {
1369     [self _setAsideSubviews];
1370     [super _recursive:recurseX displayRectIgnoringOpacity:displayRect inGraphicsContext:graphicsContext CGContext:ctx topView:isTopView shouldChangeFontReferenceColor:shouldChangeFontReferenceColor];
1371     [self _restoreSubviews];
1372 }
1373
1374 - (BOOL)_insideAnotherHTMLView
1375 {
1376     return self != [self _topHTMLView];
1377 }
1378
1379 static BOOL isQuickLookEvent(NSEvent *event)
1380 {
1381 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
1382     const int kCGSEventSystemSubtypeHotKeyCombinationReleased = 9;
1383     return [event type] == NSSystemDefined && [event subtype] == kCGSEventSystemSubtypeHotKeyCombinationReleased && [event data1] == 'lkup';
1384 #else
1385     return NO;
1386 #endif
1387 }
1388
1389 - (NSView *)hitTest:(NSPoint)point
1390 {
1391     // WebHTMLView objects handle all events for objects inside them.
1392     // To get those events, we prevent hit testing from AppKit.
1393
1394     // But there are three exceptions to this:
1395     //   1) For right mouse clicks and control clicks we don't yet have an implementation
1396     //      that works for nested views, so we let the hit testing go through the
1397     //      standard NSView code path (needs to be fixed, see bug 4361618).
1398     //   2) Java depends on doing a hit test inside it's mouse moved handling,
1399     //      so we let the hit testing go through the standard NSView code path
1400     //      when the current event is a mouse move (except when we are calling
1401     //      from _updateMouseoverWithEvent, so we have to use a global,
1402     //      forceWebHTMLViewHitTest, for that)
1403     //   3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1404     //      both need to figure out which view to check with inside the WebHTMLView.
1405     //      They use a global to change the behavior of hitTest: so they can get the
1406     //      right view. The global is forceNSViewHitTest and the method they use to
1407     //      do the hit testing is _hitViewForEvent:. (But this does not work correctly
1408     //      when there is HTML overlapping the view, see bug 4361626)
1409     //   4) NSAccessibilityHitTest relies on this for checking the cursor position.
1410     //      Our check for that is whether the event is NSFlagsChanged.  This works
1411     //      for VoiceOver's Control-Option-F5 command (move focus to item under cursor)
1412     //      and Dictionary's Command-Control-D (open dictionary popup for item under cursor).
1413     //      This is of course a hack.
1414
1415     if (_private->closed)
1416         return nil;
1417
1418     BOOL captureHitsOnSubviews;
1419     if (forceNSViewHitTest)
1420         captureHitsOnSubviews = NO;
1421     else if (forceWebHTMLViewHitTest)
1422         captureHitsOnSubviews = YES;
1423     else {
1424         // FIXME: Why doesn't this include mouse entered/exited events, or other mouse button events?
1425         NSEvent *event = [[self window] currentEvent];
1426         captureHitsOnSubviews = !([event type] == NSMouseMoved
1427             || [event type] == NSRightMouseDown
1428             || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1429             || [event type] == NSFlagsChanged
1430             || isQuickLookEvent(event));
1431     }
1432
1433     if (!captureHitsOnSubviews) {
1434         NSView* hitView = [super hitTest:point];
1435 #if USE(ACCELERATED_COMPOSITING)
1436         if (_private && hitView == _private->layerHostingView)
1437             hitView = self;
1438 #endif
1439         return hitView;
1440     }
1441     if ([[self superview] mouse:point inRect:[self frame]])
1442         return self;
1443     return nil;
1444 }
1445
1446 - (void)_clearLastHitViewIfSelf
1447 {
1448     if (lastHitView == self)
1449         lastHitView = nil;
1450 }
1451
1452 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1453 {
1454     ASSERT(_private->trackingRectOwner == nil);
1455     _private->trackingRectOwner = owner;
1456     _private->trackingRectUserData = data;
1457     return TRACKING_RECT_TAG;
1458 }
1459
1460 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1461 {
1462     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1463     ASSERT(_private->trackingRectOwner == nil);
1464     _private->trackingRectOwner = owner;
1465     _private->trackingRectUserData = data;
1466     return TRACKING_RECT_TAG;
1467 }
1468
1469 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1470 {
1471     ASSERT(count == 1);
1472     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1473     ASSERT(_private->trackingRectOwner == nil);
1474     _private->trackingRectOwner = owner;
1475     _private->trackingRectUserData = userDataList[0];
1476     trackingNums[0] = TRACKING_RECT_TAG;
1477 }
1478
1479 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1480 {
1481     if (tag == 0)
1482         return;
1483     
1484     if (_private && (tag == TRACKING_RECT_TAG)) {
1485         _private->trackingRectOwner = nil;
1486         return;
1487     }
1488     
1489     if (_private && (tag == _private->lastToolTipTag)) {
1490         [super removeTrackingRect:tag];
1491         _private->lastToolTipTag = 0;
1492         return;
1493     }
1494     
1495     // If any other tracking rect is being removed, we don't know how it was created
1496     // and it's possible there's a leak involved (see 3500217)
1497     ASSERT_NOT_REACHED();
1498 }
1499
1500 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1501 {
1502     int i;
1503     for (i = 0; i < count; ++i) {
1504         int tag = tags[i];
1505         if (tag == 0)
1506             continue;
1507         ASSERT(tag == TRACKING_RECT_TAG);
1508         if (_private != nil) {
1509             _private->trackingRectOwner = nil;
1510         }
1511     }
1512 }
1513
1514 - (void)_sendToolTipMouseExited
1515 {
1516     // Nothing matters except window, trackingNumber, and userData.
1517     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1518         location:NSMakePoint(0, 0)
1519         modifierFlags:0
1520         timestamp:0
1521         windowNumber:[[self window] windowNumber]
1522         context:NULL
1523         eventNumber:0
1524         trackingNumber:TRACKING_RECT_TAG
1525         userData:_private->trackingRectUserData];
1526     [_private->trackingRectOwner mouseExited:fakeEvent];
1527 }
1528
1529 - (void)_sendToolTipMouseEntered
1530 {
1531     // Nothing matters except window, trackingNumber, and userData.
1532     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1533         location:NSMakePoint(0, 0)
1534         modifierFlags:0
1535         timestamp:0
1536         windowNumber:[[self window] windowNumber]
1537         context:NULL
1538         eventNumber:0
1539         trackingNumber:TRACKING_RECT_TAG
1540         userData:_private->trackingRectUserData];
1541     [_private->trackingRectOwner mouseEntered:fakeEvent];
1542 }
1543
1544 - (void)_setToolTip:(NSString *)string
1545 {
1546     NSString *toolTip = [string length] == 0 ? nil : string;
1547     NSString *oldToolTip = _private->toolTip;
1548     if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1549         return;
1550     }
1551     if (oldToolTip) {
1552         [self _sendToolTipMouseExited];
1553         [oldToolTip release];
1554     }
1555     _private->toolTip = [toolTip copy];
1556     if (toolTip) {
1557         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1558         [self removeAllToolTips];
1559         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1560         _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1561         [self _sendToolTipMouseEntered];
1562     }
1563 }
1564
1565 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1566 {
1567     return [[_private->toolTip copy] autorelease];
1568 }
1569
1570 static bool mouseEventIsPartOfClickOrDrag(NSEvent *event)
1571 {
1572     switch ([event type]) {
1573         case NSLeftMouseDown:
1574         case NSLeftMouseUp:
1575         case NSLeftMouseDragged:
1576         case NSRightMouseDown:
1577         case NSRightMouseUp:
1578         case NSRightMouseDragged:
1579         case NSOtherMouseDown:
1580         case NSOtherMouseUp:
1581         case NSOtherMouseDragged:
1582             return true;
1583         default:
1584             return false;
1585     }
1586 }
1587
1588 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1589 {
1590     if (_private->closed)
1591         return;
1592
1593     NSView *contentView = [[event window] contentView];
1594     NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1595     
1596     forceWebHTMLViewHitTest = YES;
1597     NSView *hitView = [contentView hitTest:locationForHitTest];
1598     forceWebHTMLViewHitTest = NO;
1599     
1600     WebHTMLView *view = nil;
1601     if ([hitView isKindOfClass:[WebHTMLView class]])
1602         view = (WebHTMLView *)hitView;    
1603
1604     if (view)
1605         [view retain];
1606
1607     if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1608         // If we are moving out of a view (or frame), let's pretend the mouse moved
1609         // all the way out of that view. But we have to account for scrolling, because
1610         // WebCore doesn't understand our clipping.
1611         NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1612         float yScroll = visibleRect.origin.y;
1613         float xScroll = visibleRect.origin.x;
1614
1615         NSEvent *event = [NSEvent mouseEventWithType:NSMouseMoved
1616             location:NSMakePoint(-1 - xScroll, -1 - yScroll)
1617             modifierFlags:[[NSApp currentEvent] modifierFlags]
1618             timestamp:[NSDate timeIntervalSinceReferenceDate]
1619             windowNumber:[[view window] windowNumber]
1620             context:[[NSApp currentEvent] context]
1621             eventNumber:0 clickCount:0 pressure:0];
1622         if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1623             lastHitCoreFrame->eventHandler()->mouseMoved(event);
1624     }
1625
1626     lastHitView = view;
1627
1628     if (view) {
1629         if (Frame* coreFrame = core([view _frame])) {
1630             // We need to do a full, normal hit test during this mouse event if the page is active or if a mouse
1631             // button is currently pressed. It is possible that neither of those things will be true on Lion and
1632             // newer when legacy scrollbars are enabled, because then WebKit receives mouse events all the time. 
1633             // If it is one of those cases where the page is not active and the mouse is not pressed, then we can
1634             // fire a much more restricted and efficient scrollbars-only version of the event.
1635             if ([[self window] isKeyWindow] || mouseEventIsPartOfClickOrDrag(event))
1636                 coreFrame->eventHandler()->mouseMoved(event);
1637             else
1638                 coreFrame->eventHandler()->passMouseMovedEventToScrollbars(event);
1639         }
1640
1641         [view release];
1642     }
1643 }
1644
1645 + (NSArray *)_insertablePasteboardTypes
1646 {
1647     static NSArray *types = nil;
1648     if (!types) {
1649         types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType,
1650             NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
1651         CFRetain(types);
1652     }
1653     return types;
1654 }
1655
1656 + (NSArray *)_selectionPasteboardTypes
1657 {
1658     // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1659     return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1660 }
1661
1662 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1663 {
1664     [self setPromisedDragTIFFDataSource:0];
1665 }
1666
1667 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1668 {
1669     if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1670         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1671         [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1672         [archive release];
1673     } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1674         if (Image* image = [self promisedDragTIFFDataSource]->image())
1675             [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1676         [self setPromisedDragTIFFDataSource:0];
1677     }
1678 }
1679
1680 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event 
1681
1682     [self autoscroll:event]; 
1683     [self _startAutoscrollTimer:event]; 
1684
1685
1686 - (WebPluginController *)_pluginController
1687 {
1688     return _private->pluginController;
1689 }
1690
1691 - (void)_layoutForPrinting
1692 {
1693     // Set printing mode temporarily so we can adjust the size of the view. This will allow
1694     // AppKit's pagination code to use the correct height for the page content. Leaving printing
1695     // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1696     // turn it off again after adjusting the size.
1697     [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1698     [self _web_clearPrintingModeRecursive];
1699 }
1700
1701 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1702 {
1703     if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
1704         if (beforeString)
1705             *beforeString = nil;
1706         if (afterString)
1707             *afterString = nil;
1708         return;
1709     }
1710     
1711     [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
1712 }
1713
1714 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
1715 {
1716     return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
1717 }
1718
1719 - (void)_startAutoscrollTimer:(NSEvent *)triggerEvent
1720 {
1721     if (_private->autoscrollTimer == nil) {
1722         _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1723             target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1724         _private->autoscrollTriggerEvent = [triggerEvent retain];
1725     }
1726 }
1727
1728 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1729 // We can't remove this yet because it's still in use by Mail.
1730 - (NSRect)_selectionRect
1731 {
1732     return [self selectionRect];
1733 }
1734
1735 - (void)_stopAutoscrollTimer
1736 {
1737     NSTimer *timer = _private->autoscrollTimer;
1738     _private->autoscrollTimer = nil;
1739     [_private->autoscrollTriggerEvent release];
1740     _private->autoscrollTriggerEvent = nil;
1741     [timer invalidate];
1742     [timer release];
1743 }
1744
1745 - (void)_autoscroll
1746 {
1747     // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1748     // a mouse up event.
1749     BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);   
1750     if (!isStillDown){
1751         [self _stopAutoscrollTimer];
1752         return;
1753     }
1754
1755     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1756         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1757         modifierFlags:[[NSApp currentEvent] modifierFlags]
1758         timestamp:[NSDate timeIntervalSinceReferenceDate]
1759         windowNumber:[[self window] windowNumber]
1760         context:[[NSApp currentEvent] context]
1761         eventNumber:0 clickCount:0 pressure:0];
1762     [self mouseDragged:fakeEvent];
1763 }
1764
1765 - (BOOL)_canEdit
1766 {
1767     Frame* coreFrame = core([self _frame]);
1768     return coreFrame && coreFrame->editor().canEdit();
1769 }
1770
1771 - (BOOL)_canEditRichly
1772 {
1773     Frame* coreFrame = core([self _frame]);
1774     return coreFrame && coreFrame->editor().canEditRichly();
1775 }
1776
1777 - (BOOL)_canAlterCurrentSelection
1778 {
1779     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1780 }
1781
1782 - (BOOL)_hasSelection
1783 {
1784     Frame* coreFrame = core([self _frame]);
1785     return coreFrame && coreFrame->selection()->isRange();
1786 }
1787
1788 - (BOOL)_hasSelectionOrInsertionPoint
1789 {
1790     Frame* coreFrame = core([self _frame]);
1791     return coreFrame && coreFrame->selection()->isCaretOrRange();
1792 }
1793
1794 - (BOOL)_hasInsertionPoint
1795 {
1796     Frame* coreFrame = core([self _frame]);
1797     return coreFrame && coreFrame->selection()->isCaret();
1798 }
1799
1800 - (BOOL)_isEditable
1801 {
1802     Frame* coreFrame = core([self _frame]);
1803     return coreFrame && coreFrame->selection()->isContentEditable();
1804 }
1805
1806 - (BOOL)_transparentBackground
1807 {
1808     return _private->transparentBackground;
1809 }
1810
1811 - (void)_setTransparentBackground:(BOOL)f
1812 {
1813     _private->transparentBackground = f;
1814 }
1815
1816 - (NSImage *)_selectionDraggingImage
1817 {
1818     if (![self _hasSelection])
1819         return nil;
1820     NSImage *dragImage = selectionImage(core([self _frame]));
1821     [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1822     return dragImage;
1823 }
1824
1825 - (NSRect)_selectionDraggingRect
1826 {
1827     // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1828     return [self selectionRect];
1829 }
1830
1831 - (DOMNode *)_insertOrderedList
1832 {
1833     Frame* coreFrame = core([self _frame]);
1834     return coreFrame ? kit(coreFrame->editor().insertOrderedList().get()) : nil;
1835 }
1836
1837 - (DOMNode *)_insertUnorderedList
1838 {
1839     Frame* coreFrame = core([self _frame]);
1840     return coreFrame ? kit(coreFrame->editor().insertUnorderedList().get()) : nil;
1841 }
1842
1843 - (BOOL)_canIncreaseSelectionListLevel
1844 {
1845     Frame* coreFrame = core([self _frame]);
1846     return coreFrame && coreFrame->editor().canIncreaseSelectionListLevel();
1847 }
1848
1849 - (BOOL)_canDecreaseSelectionListLevel
1850 {
1851     Frame* coreFrame = core([self _frame]);
1852     return coreFrame && coreFrame->editor().canDecreaseSelectionListLevel();
1853 }
1854
1855 - (DOMNode *)_increaseSelectionListLevel
1856 {
1857     Frame* coreFrame = core([self _frame]);
1858     return coreFrame ? kit(coreFrame->editor().increaseSelectionListLevel().get()) : nil;
1859 }
1860
1861 - (DOMNode *)_increaseSelectionListLevelOrdered
1862 {
1863     Frame* coreFrame = core([self _frame]);
1864     return coreFrame ? kit(coreFrame->editor().increaseSelectionListLevelOrdered().get()) : nil;
1865 }
1866
1867 - (DOMNode *)_increaseSelectionListLevelUnordered
1868 {
1869     Frame* coreFrame = core([self _frame]);
1870     return coreFrame ? kit(coreFrame->editor().increaseSelectionListLevelUnordered().get()) : nil;
1871 }
1872
1873 - (void)_decreaseSelectionListLevel
1874 {
1875     Frame* coreFrame = core([self _frame]);
1876     if (coreFrame)
1877         coreFrame->editor().decreaseSelectionListLevel();
1878 }
1879
1880 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1881 {
1882     if (!_private->highlighters)
1883         _private->highlighters = [[NSMutableDictionary alloc] init];
1884     [_private->highlighters setObject:highlighter forKey:type];
1885 }
1886
1887 - (void)_removeHighlighterOfType:(NSString*)type
1888 {
1889     [_private->highlighters removeObjectForKey:type];
1890 }
1891
1892 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1893 {
1894     ASSERT([self _hasSelection]);
1895     NSArray *types = [self pasteboardTypesForSelection];
1896
1897     // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1898     NSAttributedString *attributedString = [self selectedAttributedString];
1899     NSMutableArray *mutableTypes = nil;
1900     if (![attributedString containsAttachments]) {
1901         mutableTypes = [types mutableCopy];
1902         [mutableTypes removeObject:NSRTFDPboardType];
1903         types = mutableTypes;
1904     }
1905
1906     [pasteboard declareTypes:types owner:[self _topHTMLView]];
1907     [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1908     [mutableTypes release];
1909 }
1910
1911 - (void)close
1912 {
1913     // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing
1914     // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
1915     if (!_private || _private->closed)
1916         return;
1917
1918     _private->closed = YES;
1919
1920     [self _clearLastHitViewIfSelf];
1921     [self _removeMouseMovedObserverUnconditionally];
1922     [self _removeWindowObservers];
1923     [self _removeSuperviewObservers];
1924     [_private->pluginController destroyAllPlugins];
1925     [_private->pluginController setDataSource:nil];
1926     // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1927     [self removeAllToolTips];
1928
1929     if (_private->isInSecureInputState) {
1930         DisableSecureEventInput();
1931         _private->isInSecureInputState = NO;
1932     }
1933
1934     [_private clear];
1935 }
1936
1937 - (BOOL)_hasHTMLDocument
1938 {
1939     Frame* coreFrame = core([self _frame]);
1940     if (!coreFrame)
1941         return NO;
1942     Document* document = coreFrame->document();
1943     return document && document->isHTMLDocument();
1944 }
1945
1946 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1947                                                  forType:(NSString *)pboardType
1948                                                inContext:(DOMRange *)context
1949                                             subresources:(NSArray **)subresources
1950 {
1951     if (pboardType == WebArchivePboardType) {
1952         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1953         if (subresources)
1954             *subresources = [archive subresources];
1955         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1956         [archive release];
1957         return fragment;
1958     }
1959     if (pboardType == NSFilenamesPboardType)
1960         return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1961         
1962     if (pboardType == NSHTMLPboardType) {
1963         NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1964         // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1965         if ([HTMLString hasPrefix:@"Version:"]) {
1966             NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
1967             if (range.location != NSNotFound)
1968                 HTMLString = [HTMLString substringFromIndex:range.location];
1969         }
1970         if ([HTMLString length] == 0)
1971             return nil;
1972         
1973         return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
1974     }
1975
1976     // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
1977     // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
1978     // FIXME: Remove this once bug 5052369 is fixed.
1979     if ([self _hasHTMLDocument] && (pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType)) {
1980         NSAttributedString *string = nil;
1981         if (pboardType == NSRTFDPboardType)
1982             string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
1983         if (string == nil)
1984             string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
1985         if (string == nil)
1986             return nil;
1987             
1988         NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
1989             [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
1990             self, @"WebResourceHandler", nil];
1991         NSArray *s;
1992         
1993         BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
1994         if (!wasDeferringCallbacks)
1995             [[self _webView] setDefersCallbacks:YES];
1996             
1997         DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length]) 
1998                                                           document:[[self _frame] DOMDocument] 
1999                                                 documentAttributes:documentAttributes
2000                                                       subresources:&s];
2001         if (subresources)
2002             *subresources = s;
2003         
2004         NSEnumerator *e = [s objectEnumerator];
2005         WebResource *r;
2006         while ((r = [e nextObject]))
2007             [[self _dataSource] addSubresource:r];
2008         
2009         if (!wasDeferringCallbacks)
2010             [[self _webView] setDefersCallbacks:NO];
2011         
2012         [documentAttributes release];
2013         [string release];
2014         return fragment;
2015     }
2016     if (pboardType == NSTIFFPboardType) {
2017         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
2018                                                               URL:uniqueURLWithRelativePart(@"image.tiff")
2019                                                          MIMEType:@"image/tiff" 
2020                                                  textEncodingName:nil
2021                                                         frameName:nil];
2022         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2023         [resource release];
2024         return fragment;
2025     }
2026     if (pboardType == NSPDFPboardType) {
2027         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPDFPboardType]
2028                                                               URL:uniqueURLWithRelativePart(@"application.pdf")
2029                                                          MIMEType:@"application/pdf" 
2030                                                  textEncodingName:nil
2031                                                         frameName:nil];
2032         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2033         [resource release];
2034         return fragment;
2035     }
2036
2037     if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) {
2038         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG]
2039                                                               URL:uniqueURLWithRelativePart(@"image.png")
2040                                                          MIMEType:@"image/png" 
2041                                                  textEncodingName:nil
2042                                                         frameName:nil];
2043         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2044         [resource release];
2045         return fragment;
2046     }
2047     if (pboardType == NSURLPboardType) {
2048         NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
2049         DOMDocument* document = [[self _frame] DOMDocument];
2050         ASSERT(document);
2051         if (!document)
2052             return nil;
2053         DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
2054         NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose.
2055         if ([URLString length] == 0)
2056             return nil;
2057         NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping];
2058         DOMText *text = [document createTextNode:URLTitleString];
2059         [anchor setHref:URLString];
2060         [anchor appendChild:text];
2061         DOMDocumentFragment *fragment = [document createDocumentFragment];
2062         [fragment appendChild:anchor];
2063         return fragment;
2064     }
2065     if (pboardType == NSStringPboardType)
2066         return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get());
2067     return nil;
2068 }
2069
2070 #if ENABLE(NETSCAPE_PLUGIN_API) 
2071 - (void)_pauseNullEventsForAllNetscapePlugins 
2072
2073     NSArray *subviews = [self subviews]; 
2074     unsigned int subviewCount = [subviews count]; 
2075     unsigned int subviewIndex; 
2076     
2077     for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { 
2078         NSView *subview = [subviews objectAtIndex:subviewIndex]; 
2079         if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) 
2080             [(WebBaseNetscapePluginView *)subview stopTimers];
2081     } 
2082
2083 #endif 
2084
2085 #if ENABLE(NETSCAPE_PLUGIN_API) 
2086 - (void)_resumeNullEventsForAllNetscapePlugins 
2087
2088     NSArray *subviews = [self subviews]; 
2089     unsigned int subviewCount = [subviews count]; 
2090     unsigned int subviewIndex; 
2091     
2092     for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { 
2093         NSView *subview = [subviews objectAtIndex:subviewIndex]; 
2094         if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) 
2095             [(WebBaseNetscapePluginView *)subview restartTimers]; 
2096     } 
2097
2098 #endif 
2099
2100 - (BOOL)_isUsingAcceleratedCompositing
2101 {
2102 #if USE(ACCELERATED_COMPOSITING)
2103     return _private->layerHostingView != nil;
2104 #else
2105     return NO;
2106 #endif
2107 }
2108
2109 - (NSView *)_compositingLayersHostingView
2110 {
2111 #if USE(ACCELERATED_COMPOSITING)
2112     return _private->layerHostingView;
2113 #else
2114     return 0;
2115 #endif
2116 }
2117
2118 - (BOOL)_isInPrintMode
2119 {
2120     return _private->printing;
2121 }
2122
2123 - (BOOL)_beginPrintModeWithMinimumPageWidth:(CGFloat)minimumPageWidth height:(CGFloat)minimumPageHeight maximumPageWidth:(CGFloat)maximumPageWidth
2124 {
2125     Frame* frame = core([self _frame]);
2126     if (!frame)
2127         return NO;
2128
2129     if (frame->document() && frame->document()->isFrameSet()) {
2130         minimumPageWidth = 0;
2131         minimumPageHeight = 0;
2132     }
2133
2134     float maximumShrinkRatio = 0;
2135     if (minimumPageWidth > 0.0)
2136         maximumShrinkRatio = maximumPageWidth / minimumPageWidth;
2137
2138     [self _setPrinting:YES minimumPageLogicalWidth:minimumPageWidth logicalHeight:minimumPageHeight originalPageWidth:minimumPageWidth originalPageHeight:minimumPageHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2139     return YES;
2140 }
2141
2142 - (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth height:(float)pageHeight shrinkToFit:(BOOL)shrinkToFit
2143 {
2144     Frame* frame = core([self _frame]);
2145     if (!frame)
2146         return NO;
2147
2148     Document* document = frame->document();
2149     bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
2150
2151     float pageLogicalWidth = isHorizontal ? pageWidth : pageHeight;
2152     float pageLogicalHeight = isHorizontal ? pageHeight : pageWidth;
2153     FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2154     float maximumShrinkRatio = 1;
2155
2156     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2157     // according to the page width.
2158     if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2159         minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2160         maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2161     }
2162
2163     [self _setPrinting:YES minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() originalPageWidth:pageLogicalWidth originalPageHeight:pageLogicalHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2164
2165     return YES;
2166 }
2167
2168 - (void)_endPrintMode
2169 {
2170     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2171 }
2172
2173 - (BOOL)_isInScreenPaginationMode
2174 {
2175     return _private->paginateScreenContent;
2176 }
2177
2178 - (BOOL)_beginScreenPaginationModeWithPageSize:(CGSize)pageSize shrinkToFit:(BOOL)shrinkToFit
2179 {
2180     Frame* frame = core([self _frame]);
2181     if (!frame)
2182         return NO;
2183
2184     Document* document = frame->document();
2185     bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
2186
2187     float pageLogicalWidth = isHorizontal ? pageSize.width : pageSize.height;
2188     float pageLogicalHeight = isHorizontal ? pageSize.height : pageSize.width;
2189     FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2190     float maximumShrinkRatio = 1;
2191
2192     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2193     // according to the page width.
2194     if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2195         minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2196         maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2197     }
2198
2199     [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() originalPageWidth:pageLogicalWidth originalPageHeight:pageLogicalHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2200
2201     return YES;
2202 }
2203
2204 - (void)_endScreenPaginationMode
2205 {
2206     [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:NO];
2207 }
2208
2209 - (CGFloat)_adjustedBottomOfPageWithTop:(CGFloat)top bottom:(CGFloat)bottom limit:(CGFloat)bottomLimit
2210 {
2211     Frame* frame = core([self _frame]);
2212     if (!frame)
2213         return bottom;
2214
2215     FrameView* view = frame->view();
2216     if (!view)
2217         return bottom;
2218
2219     float newBottom;
2220     view->adjustPageHeightDeprecated(&newBottom, top, bottom, bottomLimit);
2221
2222 #ifdef __LP64__
2223     // If the new bottom is equal to the old bottom (when both are treated as floats), we just return the original
2224     // bottom. This prevents rounding errors that can occur when converting newBottom to a double.
2225     if (fabs(static_cast<float>(bottom) - newBottom) <= std::numeric_limits<float>::epsilon()) 
2226         return bottom;
2227     else
2228 #endif
2229         return newBottom;
2230 }
2231
2232 @end
2233
2234 @implementation NSView (WebHTMLViewFileInternal)
2235
2236 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
2237 {
2238     unsigned count = [_subviews count];
2239     for (unsigned i = 0; i < count; ++i) {
2240         NSView *child = [_subviews objectAtIndex:i];
2241         if ([child isKindOfClass:[WebHTMLView class]])
2242             [array addObject:child];
2243         [child _web_addDescendantWebHTMLViewsToArray:array];
2244     }
2245 }
2246
2247 @end
2248
2249 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
2250
2251 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
2252 {
2253     if (object == nil) {
2254         [self removeObjectForKey:key];
2255     } else {
2256         [self setObject:object forKey:key];
2257     }
2258 }
2259
2260 @end
2261
2262 @implementation WebHTMLView
2263
2264 + (void)initialize
2265 {
2266     [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] 
2267                              returnTypes:[[self class] _insertablePasteboardTypes]];
2268     JSC::initializeThreading();
2269     WTF::initializeMainThreadToProcessMainThread();
2270     WebCore::RunLoop::initializeMainRunLoop();
2271     WebCoreObjCFinalizeOnMainThread(self);
2272 }
2273
2274 - (id)initWithFrame:(NSRect)frame
2275 {
2276     self = [super initWithFrame:frame];
2277     if (!self)
2278         return nil;
2279     
2280     [self setFocusRingType:NSFocusRingTypeNone];
2281     
2282     // Make all drawing go through us instead of subviews.
2283     [self _setDrawsOwnDescendants:YES];
2284     
2285     _private = [[WebHTMLViewPrivate alloc] init];
2286
2287     _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2288     
2289     return self;
2290 }
2291
2292 - (void)dealloc
2293 {
2294     if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self))
2295         return;
2296
2297     // We can't assert that close has already been called because
2298     // this view can be removed from it's superview, even though
2299     // it could be needed later, so close if needed.
2300     [self close];
2301     [_private release];
2302     _private = nil;
2303     [super dealloc];
2304 }
2305
2306 - (void)finalize
2307 {
2308     ASSERT_MAIN_THREAD();
2309     // We can't assert that close has already been called because
2310     // this view can be removed from it's superview, even though
2311     // it could be needed later, so close if needed.
2312     [self close];
2313     [super finalize];
2314 }
2315
2316 // Returns YES if the delegate returns YES (so we should do no more work).
2317 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2318 {
2319     BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2320     _private->selectorForDoCommandBySelector = 0;
2321     if (callerAlreadyCalledDelegate)
2322         return NO;
2323     WebView *webView = [self _webView];
2324     return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2325 }
2326
2327 typedef HashMap<SEL, String> SelectorNameMap;
2328
2329 // Map selectors into Editor command names.
2330 // This is not needed for any selectors that have the same name as the Editor command.
2331 static const SelectorNameMap* createSelectorExceptionMap()
2332 {
2333     SelectorNameMap* map = new HashMap<SEL, String>;
2334
2335     map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline");
2336     map->add(@selector(insertParagraphSeparator:), "InsertNewline");
2337     map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab");
2338     map->add(@selector(pageDown:), "MovePageDown");
2339     map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection");
2340     map->add(@selector(pageUp:), "MovePageUp");
2341     map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection");
2342
2343     return map;
2344 }
2345
2346 static String commandNameForSelector(SEL selector)
2347 {
2348     // Check the exception map first.
2349     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
2350     SelectorNameMap::const_iterator it = exceptionMap->find(selector);
2351     if (it != exceptionMap->end())
2352         return it->value;
2353
2354     // Remove the trailing colon.
2355     // No need to capitalize the command name since Editor command names are
2356     // not case sensitive.
2357     const char* selectorName = sel_getName(selector);
2358     size_t selectorNameLength = strlen(selectorName);
2359     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2360         return String();
2361     return String(selectorName, selectorNameLength - 1);
2362 }
2363
2364 - (Editor::Command)coreCommandBySelector:(SEL)selector
2365 {
2366     Frame* coreFrame = core([self _frame]);
2367     if (!coreFrame)
2368         return Editor::Command();
2369     return coreFrame->editor().command(commandNameForSelector(selector));
2370 }
2371
2372 - (Editor::Command)coreCommandByName:(const char*)name
2373 {
2374     Frame* coreFrame = core([self _frame]);
2375     if (!coreFrame)
2376         return Editor::Command();
2377     return coreFrame->editor().command(name);
2378 }
2379
2380 - (void)executeCoreCommandBySelector:(SEL)selector
2381 {
2382     if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2383         return;
2384     [self coreCommandBySelector:selector].execute();
2385 }
2386
2387 - (void)executeCoreCommandByName:(const char*)name
2388 {
2389     [self coreCommandByName:name].execute();
2390 }
2391
2392 // These commands are forwarded to the Editor object in WebCore.
2393 // Ideally we'd do this for all editing commands; more of the code
2394 // should be moved from here to there, and more commands should be
2395 // added to this list.
2396
2397 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2398 // The functions are identical.
2399
2400 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2401
2402 WEBCORE_COMMAND(alignCenter)
2403 WEBCORE_COMMAND(alignJustified)
2404 WEBCORE_COMMAND(alignLeft)
2405 WEBCORE_COMMAND(alignRight)
2406 WEBCORE_COMMAND(copy)
2407 WEBCORE_COMMAND(cut)
2408 WEBCORE_COMMAND(paste)
2409 WEBCORE_COMMAND(delete)
2410 WEBCORE_COMMAND(deleteBackward)
2411 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2412 WEBCORE_COMMAND(deleteForward)
2413 WEBCORE_COMMAND(deleteToBeginningOfLine)
2414 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2415 WEBCORE_COMMAND(deleteToEndOfLine)
2416 WEBCORE_COMMAND(deleteToEndOfParagraph)
2417 WEBCORE_COMMAND(deleteToMark)
2418 WEBCORE_COMMAND(deleteWordBackward)
2419 WEBCORE_COMMAND(deleteWordForward)
2420 WEBCORE_COMMAND(ignoreSpelling)
2421 WEBCORE_COMMAND(indent)
2422 WEBCORE_COMMAND(insertBacktab)
2423 WEBCORE_COMMAND(insertLineBreak)
2424 WEBCORE_COMMAND(insertNewline)
2425 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2426 WEBCORE_COMMAND(insertParagraphSeparator)
2427 WEBCORE_COMMAND(insertTab)
2428 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2429 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
2430 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
2431 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
2432 WEBCORE_COMMAND(moveBackward)
2433 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2434 WEBCORE_COMMAND(moveDown)
2435 WEBCORE_COMMAND(moveDownAndModifySelection)
2436 WEBCORE_COMMAND(moveForward)
2437 WEBCORE_COMMAND(moveForwardAndModifySelection)
2438 WEBCORE_COMMAND(moveLeft)
2439 WEBCORE_COMMAND(moveLeftAndModifySelection)
2440 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2441 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2442 WEBCORE_COMMAND(moveRight)
2443 WEBCORE_COMMAND(moveRightAndModifySelection)
2444 WEBCORE_COMMAND(moveToBeginningOfDocument)
2445 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2446 WEBCORE_COMMAND(moveToBeginningOfLine)
2447 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2448 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2449 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2450 WEBCORE_COMMAND(moveToBeginningOfSentence)
2451 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2452 WEBCORE_COMMAND(moveToEndOfDocument)
2453 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2454 WEBCORE_COMMAND(moveToEndOfLine)
2455 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2456 WEBCORE_COMMAND(moveToEndOfParagraph)
2457 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2458 WEBCORE_COMMAND(moveToEndOfSentence)
2459 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2460 WEBCORE_COMMAND(moveToLeftEndOfLine)
2461 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
2462 WEBCORE_COMMAND(moveToRightEndOfLine)
2463 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
2464 WEBCORE_COMMAND(moveUp)
2465 WEBCORE_COMMAND(moveUpAndModifySelection)
2466 WEBCORE_COMMAND(moveWordBackward)
2467 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2468 WEBCORE_COMMAND(moveWordForward)
2469 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2470 WEBCORE_COMMAND(moveWordLeft)
2471 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2472 WEBCORE_COMMAND(moveWordRight)
2473 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2474 WEBCORE_COMMAND(outdent)
2475 WEBCORE_COMMAND(overWrite)
2476 WEBCORE_COMMAND(pageDown)
2477 WEBCORE_COMMAND(pageDownAndModifySelection)
2478 WEBCORE_COMMAND(pageUp)
2479 WEBCORE_COMMAND(pageUpAndModifySelection)
2480 WEBCORE_COMMAND(pasteAsPlainText)
2481 WEBCORE_COMMAND(selectAll)
2482 WEBCORE_COMMAND(selectLine)
2483 WEBCORE_COMMAND(selectParagraph)
2484 WEBCORE_COMMAND(selectSentence)
2485 WEBCORE_COMMAND(selectToMark)
2486 WEBCORE_COMMAND(selectWord)
2487 WEBCORE_COMMAND(setMark)
2488 WEBCORE_COMMAND(subscript)
2489 WEBCORE_COMMAND(superscript)
2490 WEBCORE_COMMAND(swapWithMark)
2491 WEBCORE_COMMAND(transpose)
2492 WEBCORE_COMMAND(underline)
2493 WEBCORE_COMMAND(unscript)
2494 WEBCORE_COMMAND(yank)
2495 WEBCORE_COMMAND(yankAndSelect)
2496
2497 #undef WEBCORE_COMMAND
2498
2499 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2500
2501 - (IBAction)takeFindStringFromSelection:(id)sender
2502 {
2503     COMMAND_PROLOGUE
2504
2505     if (![self _hasSelection]) {
2506         NSBeep();
2507         return;
2508     }
2509
2510     [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2511 }
2512
2513 // This method is needed to support Mac OS X services.
2514 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2515 {
2516     [pasteboard declareTypes:types owner:[self _topHTMLView]];
2517     [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2518     return YES;
2519 }
2520
2521 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2522 {
2523     BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]);
2524     BOOL isReturnTypeOK = NO;
2525     if (!returnType)
2526         isReturnTypeOK = YES;
2527     else if ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2528         // We can insert strings in any editable context.  We can insert other types, like images, only in rich edit contexts.
2529         isReturnTypeOK = [returnType isEqualToString:NSStringPboardType] || [self _canEditRichly];
2530     }
2531     if (isSendTypeOK && isReturnTypeOK)
2532         return self;
2533     return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2534 }
2535
2536 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2537 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2538 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2539 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2540 // might be using the selector, and we don't want to break them.
2541 - (void)jumpToSelection:(id)sender
2542 {
2543     COMMAND_PROLOGUE
2544
2545     if (Frame* coreFrame = core([self _frame]))
2546         coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways);
2547 }
2548
2549 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2550 {
2551     SEL action = [item action];
2552     RefPtr<Frame> frame = core([self _frame]);
2553
2554     if (!frame)
2555         return NO;
2556     
2557     if (Document* doc = frame->document()) {
2558         if (doc->isPluginDocument())
2559             return NO;
2560         if (doc->isImageDocument()) {            
2561             if (action == @selector(copy:))
2562                 return frame->loader()->isComplete();
2563             return NO;
2564         }
2565     }
2566
2567     if (action == @selector(changeSpelling:)
2568             || action == @selector(_changeSpellingFromMenu:)
2569             || action == @selector(checkSpelling:)
2570             || action == @selector(complete:)
2571             || action == @selector(pasteFont:))
2572         return [self _canEdit];
2573
2574     if (action == @selector(showGuessPanel:)) {
2575         // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2576         NSMenuItem *menuItem = (NSMenuItem *)item;
2577         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2578             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2579             [menuItem setTitle:panelShowing
2580                 ? UI_STRING_INTERNAL("Hide Spelling and Grammar", "menu item title")
2581                 : UI_STRING_INTERNAL("Show Spelling and Grammar", "menu item title")];
2582         }
2583         return [self _canEdit];
2584     }
2585     
2586     if (action == @selector(changeBaseWritingDirection:)
2587             || action == @selector(makeBaseWritingDirectionLeftToRight:)
2588             || action == @selector(makeBaseWritingDirectionRightToLeft:)) {
2589         NSWritingDirection writingDirection;
2590
2591         if (action == @selector(changeBaseWritingDirection:)) {
2592             writingDirection = static_cast<NSWritingDirection>([item tag]);
2593             if (writingDirection == NSWritingDirectionNatural)
2594                 return NO;
2595         } else if (action == @selector(makeBaseWritingDirectionLeftToRight:))
2596             writingDirection = NSWritingDirectionLeftToRight;
2597         else
2598             writingDirection = NSWritingDirectionRightToLeft;
2599
2600         NSMenuItem *menuItem = (NSMenuItem *)item;
2601         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2602             String direction = writingDirection == NSWritingDirectionLeftToRight ? "ltr" : "rtl";
2603             [menuItem setState:frame->editor().selectionHasStyle(CSSPropertyDirection, direction)];
2604         }
2605         return [self _canEdit];
2606     }
2607
2608     if (action == @selector(makeBaseWritingDirectionNatural:)) {
2609         NSMenuItem *menuItem = (NSMenuItem *)item;
2610         if ([menuItem isKindOfClass:[NSMenuItem class]])
2611             [menuItem setState:NSOffState];
2612         return NO;
2613     }
2614
2615     if (action == @selector(toggleBaseWritingDirection:)) {
2616         NSMenuItem *menuItem = (NSMenuItem *)item;
2617         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2618             // Take control of the title of the menu item instead of just checking/unchecking it because
2619             // a check would be ambiguous.
2620             [menuItem setTitle:frame->editor().selectionHasStyle(CSSPropertyDirection, "rtl")
2621                 ? UI_STRING_INTERNAL("Left to Right", "Left to Right context menu item")
2622                 : UI_STRING_INTERNAL("Right to Left", "Right to Left context menu item")];
2623         }
2624         return [self _canEdit];
2625     } 
2626     
2627     if (action == @selector(changeAttributes:)
2628             || action == @selector(changeColor:)        
2629             || action == @selector(changeFont:))
2630         return [self _canEditRichly];
2631     
2632     if (action == @selector(capitalizeWord:)
2633                || action == @selector(lowercaseWord:)
2634                || action == @selector(uppercaseWord:))
2635         return [self _hasSelection] && [self _isEditable];
2636
2637     if (action == @selector(centerSelectionInVisibleArea:)
2638                || action == @selector(jumpToSelection:)
2639                || action == @selector(copyFont:))
2640         return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2641     
2642     if (action == @selector(changeDocumentBackgroundColor:))
2643         return [[self _webView] isEditable] && [self _canEditRichly];
2644     
2645     if (action == @selector(_ignoreSpellingFromMenu:)
2646             || action == @selector(_learnSpellingFromMenu:)
2647             || action == @selector(takeFindStringFromSelection:))
2648         return [self _hasSelection];
2649     
2650     if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2651         return frame && (frame->editor().canDHTMLPaste() || frame->editor().canPaste());
2652     
2653     if (action == @selector(pasteAsRichText:))
2654         return frame && (frame->editor().canDHTMLPaste()
2655             || (frame->editor().canPaste() && frame->selection()->isContentRichlyEditable()));
2656     
2657     if (action == @selector(performFindPanelAction:))
2658         return NO;
2659     
2660     if (action == @selector(_lookUpInDictionaryFromMenu:))
2661         return [self _hasSelection];
2662
2663     if (action == @selector(stopSpeaking:))
2664         return [NSApp isSpeaking];
2665
2666     if (action == @selector(toggleGrammarChecking:)) {
2667         // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate 
2668         // the selector here because we implement it here, and we must implement it here because the AppKit 
2669         // code checks the first responder.
2670         NSMenuItem *menuItem = (NSMenuItem *)item;
2671         if ([menuItem isKindOfClass:[NSMenuItem class]])
2672             [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2673         return YES;
2674     }
2675
2676     if (action == @selector(orderFrontSubstitutionsPanel:)) {
2677         NSMenuItem *menuItem = (NSMenuItem *)item;
2678         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2679             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
2680             [menuItem setTitle:panelShowing
2681                 ? UI_STRING_INTERNAL("Hide Substitutions", "menu item title")
2682                 : UI_STRING_INTERNAL("Show Substitutions", "menu item title")];
2683         }
2684         return [self _canEdit];
2685     }
2686     // FIXME 4799134: WebView is the bottleneck for this logic, but we must validate 
2687     // the selector here because we implement it here, and we must implement it here because the AppKit 
2688     // code checks the first responder.
2689     if (action == @selector(toggleSmartInsertDelete:)) {
2690         NSMenuItem *menuItem = (NSMenuItem *)item;
2691         if ([menuItem isKindOfClass:[NSMenuItem class]])
2692             [menuItem setState:[self smartInsertDeleteEnabled] ? NSOnState : NSOffState];
2693         return [self _canEdit];
2694     }
2695     if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
2696         NSMenuItem *menuItem = (NSMenuItem *)item;
2697         if ([menuItem isKindOfClass:[NSMenuItem class]])
2698             [menuItem setState:[self isAutomaticQuoteSubstitutionEnabled] ? NSOnState : NSOffState];
2699         return [self _canEdit];
2700     }
2701     if (action == @selector(toggleAutomaticLinkDetection:)) {
2702         NSMenuItem *menuItem = (NSMenuItem *)item;
2703         if ([menuItem isKindOfClass:[NSMenuItem class]])
2704             [menuItem setState:[self isAutomaticLinkDetectionEnabled] ? NSOnState : NSOffState];
2705         return [self _canEdit];
2706     }
2707     if (action == @selector(toggleAutomaticDashSubstitution:)) {
2708         NSMenuItem *menuItem = (NSMenuItem *)item;
2709         if ([menuItem isKindOfClass:[NSMenuItem class]])
2710             [menuItem setState:[self isAutomaticDashSubstitutionEnabled] ? NSOnState : NSOffState];
2711         return [self _canEdit];
2712     }
2713     if (action == @selector(toggleAutomaticTextReplacement:)) {
2714         NSMenuItem *menuItem = (NSMenuItem *)item;
2715         if ([menuItem isKindOfClass:[NSMenuItem class]])
2716             [menuItem setState:[self isAutomaticTextReplacementEnabled] ? NSOnState : NSOffState];
2717         return [self _canEdit];
2718     }
2719     if (action == @selector(toggleAutomaticSpellingCorrection:)) {
2720         NSMenuItem *menuItem = (NSMenuItem *)item;
2721         if ([menuItem isKindOfClass:[NSMenuItem class]])
2722             [menuItem setState:[self isAutomaticSpellingCorrectionEnabled] ? NSOnState : NSOffState];
2723         return [self _canEdit];
2724     }
2725
2726     Editor::Command command = [self coreCommandBySelector:action];
2727     if (command.isSupported()) {
2728         NSMenuItem *menuItem = (NSMenuItem *)item;
2729         if ([menuItem isKindOfClass:[NSMenuItem class]])
2730             [menuItem setState:kit(command.state())];
2731         return command.isEnabled();
2732     }
2733
2734     return YES;
2735 }
2736
2737 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2738 {
2739     // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
2740     // assumes the WebVIew is non-nil.
2741     if (![self _webView])
2742         return NO;
2743     BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2744     return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2745 }
2746
2747 - (BOOL)acceptsFirstResponder
2748 {
2749     // Don't accept first responder when we first click on this view.
2750     // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2751     // Do accept first responder at any other time, for example from keyboard events,
2752     // or from calls back from WebCore once we begin mouse-down event handling.
2753     NSEvent *event = [NSApp currentEvent];
2754     if ([event type] == NSLeftMouseDown
2755             && !_private->handlingMouseDownEvent
2756             && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2757         return NO;
2758     }
2759     return YES;
2760 }
2761
2762 - (BOOL)maintainsInactiveSelection
2763 {
2764     // This method helps to determine whether the WebHTMLView should maintain
2765     // an inactive selection when it's not first responder.
2766     // Traditionally, these views have not maintained such selections,
2767     // clearing them when the view was not first responder. However,
2768     // to fix bugs like this one:
2769     // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even 
2770     //                            when they're not firstResponder"
2771     // it was decided to add a switch to act more like an NSTextView.
2772
2773     if ([[self _webView] maintainsInactiveSelection])
2774         return YES;
2775
2776     // Predict the case where we are losing first responder status only to
2777     // gain it back again. Want to keep the selection in that case.
2778     id nextResponder = [[self window] _newFirstResponderAfterResigning];
2779     if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2780         id contentView = [nextResponder contentView];
2781         if (contentView)
2782             nextResponder = contentView;
2783     }
2784     if ([nextResponder isKindOfClass:[NSClipView class]]) {
2785         id documentView = [nextResponder documentView];
2786         if (documentView)
2787             nextResponder = documentView;
2788     }
2789     if (nextResponder == self)
2790         return YES;
2791
2792     Frame* coreFrame = core([self _frame]);
2793     bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable();
2794     bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2795         && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2796
2797     return selectionIsEditable && nextResponderIsInWebView;
2798 }
2799
2800 - (void)addMouseMovedObserver
2801 {
2802     if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications)
2803         return;
2804
2805     // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2806     if (!([[self window] isKeyWindow] 
2807 #if ENABLE(DASHBOARD_SUPPORT)
2808             || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]
2809 #endif
2810         ))
2811         return;
2812
2813     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2814         name:WKMouseMovedNotification() object:nil];
2815     [self _frameOrBoundsChanged];
2816     _private->observingMouseMovedNotifications = true;
2817 }
2818
2819 - (void)removeMouseMovedObserver
2820 {
2821 #if ENABLE(DASHBOARD_SUPPORT)
2822     // Don't remove the observer if we're running the Dashboard.
2823     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2824         return;
2825 #endif
2826
2827 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
2828     // Legacy scrollbars require tracking the mouse at all times.
2829     if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy)
2830         return;
2831 #endif
2832
2833     [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2834     [self _removeMouseMovedObserverUnconditionally];
2835 }
2836
2837 - (void)addSuperviewObservers
2838 {
2839     if (_private->observingSuperviewNotifications)
2840         return;
2841
2842     NSView *superview = [self superview];
2843     if (!superview || ![self window])
2844         return;
2845     
2846     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2847     [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview];
2848     [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview];
2849     
2850     // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2851     // It will check the current scroll against the previous layout's scroll.  We need to
2852     // do this here to catch the case where the WebView is laid out at one size, removed from its
2853     // window, resized, and inserted into another window.  Our frame/bounds changed notifications
2854     // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2855     [self _frameOrBoundsChanged];
2856     
2857     _private->observingSuperviewNotifications = true;
2858 }
2859
2860 - (void)addWindowObservers
2861 {
2862     if (_private->observingWindowNotifications)
2863         return;
2864     
2865     NSWindow *window = [self window];
2866     if (!window)
2867         return;
2868     
2869     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2870     [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
2871     [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
2872     [notificationCenter addObserver:self selector:@selector(windowWillOrderOnScreen:) name:WKWindowWillOrderOnScreenNotification() object:window];
2873     [notificationCenter addObserver:self selector:@selector(windowWillOrderOffScreen:) name:WKWindowWillOrderOffScreenNotification() object:window];
2874     [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
2875     
2876     _private->observingWindowNotifications = true;
2877 }
2878
2879 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2880 {
2881     [self _removeSuperviewObservers];
2882 }
2883
2884 - (void)viewDidMoveToSuperview
2885 {
2886     if ([self superview] != nil)
2887         [self addSuperviewObservers];
2888
2889 #if USE(ACCELERATED_COMPOSITING)
2890     if ([self superview] && [self _isUsingAcceleratedCompositing]) {
2891         WebView *webView = [self _webView];
2892         if ([webView _postsAcceleratedCompositingNotifications])
2893             [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:webView userInfo:nil];
2894     }
2895 #endif
2896 }
2897
2898 - (void)viewWillMoveToWindow:(NSWindow *)window
2899 {
2900     // Don't do anything if we aren't initialized.  This happens
2901     // when decoding a WebView.  When WebViews are decoded their subviews
2902     // are created by initWithCoder: and so won't be normally
2903     // initialized.  The stub views are discarded by WebView.
2904     if (!_private)
2905         return;
2906
2907     // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2908     [self _removeMouseMovedObserverUnconditionally];
2909     [self _removeWindowObservers];
2910     [self _removeSuperviewObservers];
2911
2912     // FIXME: This accomplishes the same thing as the call to setCanStartMedia(false) in
2913     // WebView. It would be nice to have a single mechanism instead of two.
2914     [[self _pluginController] stopAllPlugins];
2915 }
2916
2917 - (void)viewDidMoveToWindow
2918 {
2919     // Don't do anything if we aren't initialized.  This happens
2920     // when decoding a WebView.  When WebViews are decoded their subviews
2921     // are created by initWithCoder: and so won't be normally
2922     // initialized.  The stub views are discarded by WebView.
2923     if (!_private || _private->closed)
2924         return;
2925         
2926     [self _stopAutoscrollTimer];
2927     if ([self window]) {
2928         _private->lastScrollPosition = [[self superview] bounds].origin;
2929         [self addWindowObservers];
2930         [self addSuperviewObservers];
2931         [self addMouseMovedObserver];
2932
2933         // FIXME: This accomplishes the same thing as the call to setCanStartMedia(true) in
2934         // WebView. It would be nice to have a single mechanism instead of two.
2935         [[self _pluginController] startAllPlugins];
2936
2937         _private->lastScrollPosition = NSZeroPoint;
2938     }
2939 }
2940
2941 - (void)_web_makePluginSubviewsPerformSelector:(SEL)selector withObject:(id)object
2942 {
2943 #if ENABLE(NETSCAPE_PLUGIN_API)
2944     // Copy subviews because [self subviews] returns the view's mutable internal array,
2945     // and we must avoid mutating the array while enumerating it.
2946     NSArray *subviews = [[self subviews] copy];
2947     
2948     NSEnumerator *enumerator = [subviews objectEnumerator];
2949     WebNetscapePluginView *view;
2950     while ((view = [enumerator nextObject]) != nil)
2951         if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
2952             [view performSelector:selector withObject:object];
2953     
2954     [subviews release];
2955 #endif
2956 }
2957
2958 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2959 {
2960     [self _web_makePluginSubviewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2961 }
2962
2963 - (void)viewDidMoveToHostWindow
2964 {
2965     [self _web_makePluginSubviewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2966 }
2967
2968
2969 - (void)addSubview:(NSView *)view
2970 {
2971     [super addSubview:view];
2972
2973     if ([WebPluginController isPlugInView:view])
2974         [[self _pluginController] addPlugin:view];
2975 }
2976
2977 - (void)willRemoveSubview:(NSView *)subview
2978 {
2979 #ifndef NDEBUG
2980     // Have to null-check _private, since this can be called via -dealloc when
2981     // cleaning up the the layerHostingView.
2982     if (_private && _private->enumeratingSubviews)
2983         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]));
2984 #endif
2985
2986     if ([WebPluginController isPlugInView:subview])
2987         [[self _pluginController] destroyPlugin:subview];
2988
2989     [super willRemoveSubview:subview];
2990 }
2991
2992 - (void)reapplyStyles
2993 {
2994 #ifdef LOG_TIMES
2995     double start = CFAbsoluteTimeGetCurrent();
2996 #endif
2997
2998     if (Frame* coreFrame = core([self _frame]))
2999         coreFrame->document()->styleResolverChanged(RecalcStyleImmediately);
3000     
3001 #ifdef LOG_TIMES        
3002     double thisTime = CFAbsoluteTimeGetCurrent() - start;
3003     LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
3004 #endif
3005 }
3006
3007 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
3008 // minPageWidth==0 implies a non-printing layout
3009 - (void)layoutToMinimumPageWidth:(float)minPageLogicalWidth height:(float)minPageLogicalHeight originalPageWidth:(float)originalPageWidth originalPageHeight:(float)originalPageHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustingViewSize:(BOOL)adjustViewSize
3010 {    
3011     if (![self _needsLayout])
3012         return;
3013
3014 #ifdef LOG_TIMES        
3015     double start = CFAbsoluteTimeGetCurrent();
3016 #endif
3017
3018     LOG(View, "%@ doing layout", self);
3019
3020     Frame* coreFrame = core([self _frame]);
3021     if (!coreFrame)
3022         return;
3023
3024     if (FrameView* coreView = coreFrame->view()) {
3025         if (minPageLogicalWidth > 0.0) {
3026             FloatSize pageSize(minPageLogicalWidth, minPageLogicalHeight);
3027             FloatSize originalPageSize(originalPageWidth, originalPageHeight);
3028             if (coreFrame->document() && coreFrame->document()->renderView() && !coreFrame->document()->renderView()->style()->isHorizontalWritingMode()) {
3029                 pageSize = FloatSize(minPageLogicalHeight, minPageLogicalWidth);
3030                 originalPageSize = FloatSize(originalPageHeight, originalPageWidth);
3031             }
3032             coreView->forceLayoutForPagination(pageSize, originalPageSize, maximumShrinkRatio, adjustViewSize ? AdjustViewSize : DoNotAdjustViewSize);
3033         } else {
3034             coreView->forceLayout(!adjustViewSize);
3035             if (adjustViewSize)
3036                 coreView->adjustViewSize();
3037         }
3038     }
3039     
3040 #ifdef LOG_TIMES        
3041     double thisTime = CFAbsoluteTimeGetCurrent() - start;
3042     LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
3043 #endif
3044 }
3045
3046 - (void)layout
3047 {
3048     [self layoutToMinimumPageWidth:0 height:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustingViewSize:NO];
3049 }
3050
3051 // Deliver mouseup events to the DOM for button 2.
3052 - (void)rightMouseUp:(NSEvent *)event
3053 {
3054     // There's a chance that if we run a nested event loop the event will be released.
3055     // Retaining and then autoreleasing prevents that from causing a problem later here or
3056     // inside AppKit code.
3057     [[event retain] autorelease];
3058
3059     [super rightMouseUp:event];
3060
3061     if (Frame* coreframe = core([self _frame]))
3062         coreframe->eventHandler()->mouseUp(event);
3063 }
3064
3065 static void setMenuItemTarget(NSMenuItem* menuItem)
3066 {
3067     // Don't set the menu item's action to the context menu action forwarder if we already
3068     // have an action.
3069     if ([menuItem action])
3070         return;
3071
3072     [menuItem setTarget:[WebMenuTarget sharedMenuTarget]];
3073     [menuItem setAction:@selector(forwardContextMenuAction:)];
3074 }
3075
3076 static void setMenuTargets(NSMenu* menu)
3077 {
3078     NSInteger itemCount = [menu numberOfItems];
3079     for (NSInteger i = 0; i < itemCount; ++i) {
3080         NSMenuItem *item = [menu itemAtIndex:i];
3081         setMenuItemTarget(item);
3082         if ([item hasSubmenu])
3083             setMenuTargets([item submenu]);
3084     }
3085 }
3086
3087 - (NSMenu *)menuForEvent:(NSEvent *)event
3088 {
3089     // There's a chance that if we run a nested event loop the event will be released.
3090     // Retaining and then autoreleasing prevents that from causing a problem later here or
3091     // inside AppKit code.
3092     [[event retain] autorelease];
3093
3094     [_private->completionController endRevertingChange:NO moveLeft:NO];
3095
3096     RefPtr<Frame> coreFrame = core([self _frame]);
3097     if (!coreFrame)
3098         return nil;
3099
3100     Page* page = coreFrame->page();
3101     if (!page)
3102         return nil;
3103
3104     // Match behavior of other browsers by sending a mousedown event for right clicks.
3105     _private->handlingMouseDownEvent = YES;
3106     page->contextMenuController()->clearContextMenu();
3107     coreFrame->eventHandler()->mouseDown(event);
3108     BOOL handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformEventFactory::createPlatformMouseEvent(event, page->chrome().platformPageClient()));
3109     _private->handlingMouseDownEvent = NO;
3110
3111     if (!handledEvent)
3112         return nil;
3113
3114     // Re-get page, since it might have gone away during event handling.
3115     page = coreFrame->page();
3116     if (!page)
3117         return nil;
3118
3119     ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
3120     if (!coreMenu)
3121         return nil;
3122
3123     NSArray* menuItems = coreMenu->platformDescription();
3124     if (!menuItems)
3125         return nil;
3126
3127     NSUInteger count = [menuItems count];
3128     if (!count)
3129         return nil;
3130
3131     NSMenu* menu = [[[NSMenu alloc] init] autorelease];
3132     for (NSUInteger i = 0; i < count; i++)
3133         [menu addItem:[menuItems objectAtIndex:i]];
3134     setMenuTargets(menu);
3135     
3136     [[WebMenuTarget sharedMenuTarget] setMenuController:page->contextMenuController()];
3137     
3138     return menu;
3139 }
3140
3141 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
3142 {
3143     return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
3144 }
3145
3146 - (void)clearFocus
3147 {
3148     Frame* coreFrame = core([self _frame]);
3149     if (!coreFrame)
3150         return;
3151     Document* document = coreFrame->document();
3152     if (!document)
3153         return;
3154     
3155     document->setFocusedElement(0);
3156 }
3157
3158 - (BOOL)isOpaque
3159 {
3160     return [[self _webView] drawsBackground];
3161 }
3162
3163 - (void)setLayer:(CALayer *)layer
3164 {
3165     if (Frame* frame = core([self _frame])) {
3166         if (FrameView* view = frame->view())
3167             view->setPaintsEntireContents(layer);
3168     }
3169
3170     [super setLayer:layer];
3171 }
3172
3173 #if !LOG_DISABLED
3174 - (void)setNeedsDisplay:(BOOL)flag
3175 {
3176     LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO");
3177     [super setNeedsDisplay:flag];
3178 }
3179 #endif
3180
3181 - (void)setNeedsDisplayInRect:(NSRect)invalidRect
3182 {
3183     if (_private->inScrollPositionChanged) {
3184         // When scrolling, the dirty regions are adjusted for the scroll only
3185         // after NSViewBoundsDidChangeNotification is sent. Translate the invalid
3186         // rect to pre-scrolled coordinates in order to get the right dirty region
3187         // after adjustment. See <rdar://problem/7678927>.
3188         NSPoint origin = [[self superview] bounds].origin;
3189         invalidRect.origin.x -= _private->lastScrollPosition.x - origin.x;
3190         invalidRect.origin.y -= _private->lastScrollPosition.y - origin.y;
3191     }
3192     [super setNeedsDisplayInRect:invalidRect];
3193 }
3194
3195 - (void)setNeedsLayout: (BOOL)flag
3196 {
3197     LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO");
3198     if (!flag)
3199         return; // There's no way to say you don't need a layout.
3200     if (Frame* frame = core([self _frame])) {
3201         if (frame->document() && frame->document()->inPageCache())
3202             return;
3203         if (FrameView* view = frame->view())
3204             view->setNeedsLayout();
3205     }
3206 }
3207
3208 - (void)setNeedsToApplyStyles: (BOOL)flag
3209 {
3210     LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO");
3211     if (!flag)
3212         return; // There's no way to say you don't need a style recalc.
3213     if (Frame* frame = core([self _frame])) {
3214         if (frame->document() && frame->document()->inPageCache())
3215             return;
3216         frame->document()->scheduleForcedStyleRecalc();
3217     }
3218 }
3219
3220 - (void)drawSingleRect:(NSRect)rect
3221 {
3222     [NSGraphicsContext saveGraphicsState];
3223     NSRectClip(rect);
3224         
3225     ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
3226
3227     [(WebClipView *)[self superview] setAdditionalClip:rect];
3228
3229     @try {
3230         if ([self _transparentBackground]) {
3231             [[NSColor clearColor] set];
3232             NSRectFill (rect);
3233         }
3234
3235         [[self _frame] _drawRect:rect contentsOnly:YES];
3236
3237         WebView *webView = [self _webView];
3238
3239         // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
3240         // called after the WebView has closed. If the client did not properly close the WebView and set the 
3241         // UIDelegate to nil, then the UIDelegate will be stale and this code will crash. 
3242         static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
3243         if (version3OrLaterClient)
3244             [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
3245
3246         if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight])
3247             [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]];
3248
3249         [(WebClipView *)[self superview] resetAdditionalClip];
3250
3251         [NSGraphicsContext restoreGraphicsState];
3252     } @catch (NSException *localException) {
3253         [(WebClipView *)[self superview] resetAdditionalClip];
3254         [NSGraphicsContext restoreGraphicsState];
3255         LOG_ERROR("Exception caught while drawing: %@", localException);
3256         [localException raise];
3257     }
3258 }
3259
3260 - (void)drawRect:(NSRect)rect
3261 {
3262     ASSERT_MAIN_THREAD();
3263     LOG(View, "%@ drawing", self);
3264
3265     const NSRect *rects;
3266     NSInteger count;
3267     [self getRectsBeingDrawn:&rects count:&count];
3268
3269     BOOL subviewsWereSetAside = _private->subviewsSetAside;
3270     if (subviewsWereSetAside)
3271         [self _restoreSubviews];
3272
3273 #ifdef LOG_TIMES
3274     double start = CFAbsoluteTimeGetCurrent();
3275 #endif
3276
3277     // If count == 0 here, use the rect passed in for drawing. This is a workaround for: 
3278     // <rdar://problem/3908282> REGRESSION (Mail): No drag image dragging selected text in Blot and Mail 
3279     // The reason for the workaround is that this method is called explicitly from the code 
3280     // to generate a drag image, and at that time, getRectsBeingDrawn:count: will return a zero count. 
3281     const int cRectThreshold = 10; 
3282     const float cWastedSpaceThreshold = 0.75f; 
3283     BOOL useUnionedRect = (count <= 1) || (count > cRectThreshold); 
3284     if (!useUnionedRect) { 
3285         // Attempt to guess whether or not we should use the unioned rect or the individual rects. 
3286         // We do this by computing the percentage of "wasted space" in the union.  If that wasted space 
3287         // is too large, then we will do individual rect painting instead. 
3288         float unionPixels = (rect.size.width * rect.size.height); 
3289         float singlePixels = 0; 
3290         for (int i = 0; i < count; ++i) 
3291             singlePixels += rects[i].size.width * rects[i].size.height; 
3292         float wastedSpace = 1 - (singlePixels / unionPixels); 
3293         if (wastedSpace <= cWastedSpaceThreshold) 
3294             useUnionedRect = YES; 
3295     }
3296
3297     if (useUnionedRect) 
3298         [self drawSingleRect:rect];
3299     else {
3300         for (int i = 0; i < count; ++i)
3301             [self drawSingleRect:rects[i]];
3302     }
3303
3304 #ifdef LOG_TIMES
3305     double thisTime = CFAbsoluteTimeGetCurrent() - start;
3306     LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
3307 #endif
3308
3309     if (subviewsWereSetAside)
3310         [self _setAsideSubviews];
3311
3312     WebView *webView = [self _webView];
3313
3314 #if USE(ACCELERATED_COMPOSITING)
3315     // Only do the synchronization dance if we're drawing into the window, otherwise
3316     // we risk disabling screen updates when no flush is pending.
3317     if ([NSGraphicsContext currentContext] == [[self window] graphicsContext] && [webView _needsOneShotDrawingSynchronization]) {
3318         // Disable screen updates to minimize the chances of the race between the CA
3319         // display link and AppKit drawing causing flashes.
3320         [[self window] disableScreenUpdatesUntilFlush];
3321         
3322         // Make sure any layer changes that happened as a result of layout
3323         // via -viewWillDraw are committed.
3324         [CATransaction flush];
3325         [webView _setNeedsOneShotDrawingSynchronization:NO];
3326     }
3327 #endif
3328
3329     if (webView)
3330         CallUIDelegate(webView, @selector(webView:didDrawFrame:), [self _frame]);
3331 }
3332
3333 // Turn off the additional clip while computing our visibleRect.
3334 - (NSRect)visibleRect
3335 {
3336     if (!([[self superview] isKindOfClass:[WebClipView class]]))
3337         return [super visibleRect];
3338
3339     WebClipView *clipView = (WebClipView *)[self superview];
3340
3341     BOOL hasAdditionalClip = [clipView hasAdditionalClip];
3342     if (!hasAdditionalClip) {
3343         return [super visibleRect];
3344     }
3345     
3346     NSRect additionalClip = [clipView additionalClip];
3347     [clipView resetAdditionalClip];
3348     NSRect visibleRect = [super visibleRect];
3349     [clipView setAdditionalClip:additionalClip];
3350     return visibleRect;
3351 }
3352
3353 - (void)_invalidateGStatesForTree
3354 {
3355     // AppKit is in the process of traversing the NSView tree, and is going to send -renewGState to
3356     // descendants, including plug-in views. This can result in calls out to plug-in code and back into
3357     // WebCore via JavaScript, which could normally mutate the NSView tree while it is being traversed.
3358     // Defer those mutations while descendants are being traveresed.
3359     WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
3360     [super _invalidateGStatesForTree];
3361 }
3362
3363 - (BOOL)isFlipped 
3364 {
3365     return YES;
3366 }
3367
3368 - (void)windowDidBecomeKey:(NSNotification *)notification
3369 {
3370     if (!pthread_main_np()) {
3371         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3372         return;
3373     }
3374
3375 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
3376     if (_private->trackingAreaForNonKeyWindow) {
3377         [self removeTrackingArea:_private->trackingAreaForNonKeyWindow];
3378         [_private->trackingAreaForNonKeyWindow release];
3379         _private->trackingAreaForNonKeyWindow = nil;
3380     }
3381 #endif
3382
3383     NSWindow *keyWindow = [notification object];
3384
3385     if (keyWindow == [self window]) {
3386         [self addMouseMovedObserver];
3387         [self _updateSecureInputState];
3388     }
3389 }
3390
3391 - (void)windowDidResignKey:(NSNotification *)notification
3392 {
3393     if (!pthread_main_np()) {
3394         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3395         return;
3396     }
3397
3398     NSWindow *formerKeyWindow = [notification object];
3399
3400     if (formerKeyWindow == [self window])
3401         [self removeMouseMovedObserver];
3402
3403     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
3404         [self _updateSecureInputState];
3405         [_private->completionController endRevertingChange:NO moveLeft:NO];
3406     }
3407
3408 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
3409     if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy) {
3410         // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
3411         // It's easiest to do this with a tracking area, which we will remove when the window is key
3412         // again.
3413         _private->trackingAreaForNonKeyWindow = [[NSTrackingArea alloc] initWithRect:[self bounds]
3414                                                     options:NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingInVisibleRect | NSTrackingActiveAlways
3415                                                     owner:self
3416                                                     userInfo:nil];
3417         [self addTrackingArea:_private->trackingAreaForNonKeyWindow];
3418     }
3419 #endif
3420 }
3421
3422 - (void)windowWillOrderOnScreen:(NSNotification *)notification
3423 {
3424     if (!pthread_main_np()) {
3425         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3426         return;
3427     }
3428
3429     // Check if the window is already a key window, which can be the case for NSPopovers.
3430     if ([[self window] isKeyWindow])
3431         [self addMouseMovedObserver];
3432 }
3433
3434 - (void)windowWillOrderOffScreen:(NSNotification *)notification
3435 {
3436     if (!pthread_main_np()) {
3437         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3438         return;
3439     }
3440
3441     // When the WebView is in a NSPopover the NSWindowDidResignKeyNotification isn't sent
3442     // unless the parent window loses key. So we need to remove the mouse moved observer.
3443     [self removeMouseMovedObserver];
3444 }
3445
3446 - (void)windowWillClose:(NSNotification *)notification
3447 {
3448     if (!pthread_main_np()) {
3449         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3450         return;
3451     }
3452
3453     [_private->completionController endRevertingChange:NO moveLeft:NO];
3454     [[self _pluginController] destroyAllPlugins];
3455 }
3456
3457 - (void)scrollWheel:(NSEvent *)event
3458 {
3459     // There's a chance that responding to this event will run a nested event loop, and
3460     // fetching a new event might release the old one. Retaining and then autoreleasing
3461     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3462     [[event retain] autorelease];
3463
3464     Frame* frame = core([self _frame]);
3465     if (!frame || !frame->eventHandler()->wheelEvent(event))
3466         [super scrollWheel:event];
3467 }
3468
3469 - (BOOL)_isSelectionEvent:(NSEvent *)event
3470 {
3471     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3472     return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
3473 }
3474
3475 - (BOOL)_isScrollBarEvent:(NSEvent *)event
3476 {
3477     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3478     return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsInScrollBarKey] boolValue];
3479 }
3480
3481 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3482 {
3483     // There's a chance that responding to this event will run a nested event loop, and
3484     // fetching a new event might release the old one. Retaining and then autoreleasing
3485     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3486     [[event retain] autorelease];
3487
3488     NSView *hitView = [self _hitViewForEvent:event];
3489     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3490     
3491 #if ENABLE(DASHBOARD_SUPPORT)
3492     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
3493         return YES;
3494 #endif
3495     
3496     if (hitHTMLView) {
3497         bool result = false;
3498         if (Frame* coreFrame = core([hitHTMLView _frame])) {
3499             coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
3500             [hitHTMLView _setMouseDownEvent:event];
3501             if ([hitHTMLView _isSelectionEvent:event]) {
3502 #if ENABLE(DRAG_SUPPORT)
3503                 if (Page* page = coreFrame->page())
3504                     result = coreFrame->eventHandler()->eventMayStartDrag(PlatformEventFactory::createPlatformMouseEvent(event, page->chrome().platformPageClient()));
3505 #endif
3506             } else if ([hitHTMLView _isScrollBarEvent:event])
3507                 result = true;
3508             [hitHTMLView _setMouseDownEvent:nil];
3509         }
3510         return result;
3511     }
3512     return [hitView acceptsFirstMouse:event];
3513 }
3514
3515 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3516 {
3517     // There's a chance that responding to this event will run a nested event loop, and
3518     // fetching a new event might release the old one. Retaining and then autoreleasing
3519     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3520     [[event retain] autorelease];
3521
3522     NSView *hitView = [self _hitViewForEvent:event];
3523     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3524     if (hitHTMLView) {
3525         bool result = false;
3526         if ([hitHTMLView _isSelectionEvent:event]) {
3527             [hitHTMLView _setMouseDownEvent:event];
3528 #if ENABLE(DRAG_SUPPORT)
3529             if (Frame* coreFrame = core([hitHTMLView _frame])) {
3530                 if (Page* page = coreFrame->page())
3531                     result = coreFrame->eventHandler()->eventMayStartDrag(PlatformEventFactory::createPlatformMouseEvent(event, page->chrome().platformPageClient()));
3532             }
3533 #endif
3534             [hitHTMLView _setMouseDownEvent:nil];
3535         }
3536         return result;
3537     }
3538     return [hitView shouldDelayWindowOrderingForEvent:event];
3539 }
3540
3541 - (void)mouseDown:(NSEvent *)event
3542 {
3543     // There's a chance that responding to this event will run a nested event loop, and
3544     // fetching a new event might release the old one. Retaining and then autoreleasing
3545     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3546     [[event retain] autorelease];
3547
3548     RetainPtr<WebHTMLView> protector = self;
3549     if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
3550         return;
3551
3552     _private->handlingMouseDownEvent = YES;
3553
3554     // Record the mouse down position so we can determine drag hysteresis.
3555     [self _setMouseDownEvent:event];
3556
3557     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3558
3559     if (![currentInputManager wantsToHandleMouseEvents] || ![currentInputManager handleMouseEvent:event]) {
3560         [_private->completionController endRevertingChange:NO moveLeft:NO];
3561
3562         // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
3563         // We don't want to pass them along to KHTML a second time.
3564         if (!([event modifierFlags] & NSControlKeyMask)) {
3565             _private->ignoringMouseDraggedEvents = NO;
3566
3567             // Let WebCore get a chance to deal with the event. This will call back to us
3568             // to start the autoscroll timer if appropriate.
3569             if (Frame* coreframe = core([self _frame]))
3570                 coreframe->eventHandler()->mouseDown(event);
3571         }
3572     }
3573
3574     _private->handlingMouseDownEvent = NO;
3575 }
3576
3577 #if ENABLE(DRAG_SUPPORT)
3578 - (void)dragImage:(NSImage *)dragImage
3579                at:(NSPoint)at
3580            offset:(NSSize)offset
3581             event:(NSEvent *)event
3582        pasteboard:(NSPasteboard *)pasteboard
3583            source:(id)source
3584         slideBack:(BOOL)slideBack
3585 {
3586     ASSERT(self == [self _topHTMLView]);
3587     [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
3588 }
3589
3590 - (void)mouseDragged:(NSEvent *)event
3591 {
3592     // There's a chance that responding to this event will run a nested event loop, and
3593     // fetching a new event might release the old one. Retaining and then autoreleasing
3594     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3595     [[event retain] autorelease];
3596
3597     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3598     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3599         return;
3600
3601     [self retain];
3602
3603     if (!_private->ignoringMouseDraggedEvents) {
3604         if (Frame* frame = core([self _frame])) {
3605             if (Page* page = frame->page())
3606                 page->mainFrame()->eventHandler()->mouseDragged(event);
3607         }
3608     }
3609
3610     [self release];
3611 }
3612
3613 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
3614 {
3615     ASSERT(![self _webView] || [self _isTopHTMLView]);
3616     
3617     Page* page = core([self _webView]);
3618     if (!page)
3619         return NSDragOperationNone;
3620
3621     return (NSDragOperation)page->dragController()->sourceDragOperation();
3622 }
3623
3624 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
3625 {
3626     ASSERT(![self _webView] || [self _isTopHTMLView]);
3627     
3628     NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
3629     NSPoint windowMouseLoc = windowImageLoc;
3630     
3631     if (Page* page = core([self _webView])) {
3632         DragController* dragController = page->dragController();
3633         windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3634         dragController->dragEnded();
3635     }
3636     
3637     [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation];
3638     
3639     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3640     _private->ignoringMouseDraggedEvents = YES;
3641     
3642     // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
3643     // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
3644     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
3645                                             location:windowMouseLoc
3646                                        modifierFlags:[[NSApp currentEvent] modifierFlags]
3647                                            timestamp:[NSDate timeIntervalSinceReferenceDate]
3648                                         windowNumber:[[self window] windowNumber]
3649                                              context:[[NSApp currentEvent] context]
3650                                          eventNumber:0 clickCount:0 pressure:0];
3651     [self mouseUp:fakeEvent]; // This will also update the mouseover state.
3652 }
3653
3654 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
3655 {
3656     NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
3657     return [filename _webkit_hasCaseInsensitiveSuffix:extensionAsSuffix]
3658     || ([extension _webkit_isCaseInsensitiveEqualToString:@"jpeg"]
3659         && [filename _webkit_hasCaseInsensitiveSuffix:@".jpg"]);
3660 }
3661
3662 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3663 {
3664     NSFileWrapper *wrapper = nil;
3665     NSURL *draggingImageURL = nil;
3666     
3667     if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) {
3668         if (ResourceBuffer *buffer = static_cast<CachedResource*>(tiffResource)->resourceBuffer()) {
3669             NSData *data = buffer->createNSData();
3670             NSURLResponse *response = tiffResource->response().nsURLResponse();
3671             draggingImageURL = [response URL];
3672             wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
3673             NSString* filename = [response suggestedFilename];
3674             NSString* trueExtension(tiffResource->image()->filenameExtension());
3675             if (!matchesExtensionOrEquivalent(filename, trueExtension))
3676                 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension];
3677             [wrapper setPreferredFilename:filename];
3678         }
3679     }
3680     
3681     if (!wrapper) {
3682         ASSERT(![self _webView] || [self _isTopHTMLView]);
3683         Page* page = core([self _webView]);
3684         
3685         //If a load occurs midway through a drag, the view may be detached, which gives
3686         //us no ability to get to the original Page, so we cannot access any drag state
3687         //FIXME: is there a way to recover?
3688         if (!page) 
3689             return nil; 
3690         
3691         const KURL& imageURL = page->dragController()->draggingImageURL();
3692         ASSERT(!imageURL.isEmpty());
3693         draggingImageURL = imageURL;
3694
3695         wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL];
3696     }
3697     
3698     if (wrapper == nil) {
3699         LOG_ERROR("Failed to create image file.");
3700         return nil;
3701     }
3702
3703     // FIXME: Report an error if we fail to create a file.
3704     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3705     path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
3706     if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
3707         LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
3708     
3709     if (draggingImageURL)
3710         [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path];
3711     
3712     return [NSArray arrayWithObject:[path lastPathComponent]];
3713 }
3714 #endif
3715
3716 - (void)mouseUp:(NSEvent *)event
3717 {
3718     // There's a chance that responding to this event will run a nested event loop, and
3719     // fetching a new event might release the old one. Retaining and then autoreleasing
3720     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3721     [[event retain] autorelease];
3722
3723     [self _setMouseDownEvent:nil];
3724
3725     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3726     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3727         return;
3728
3729     [self retain];
3730
3731     [self _stopAutoscrollTimer];
3732     if (Frame* frame = core([self _frame])) {
3733         if (Page* page = frame->page())
3734             page->mainFrame()->eventHandler()->mouseUp(event);
3735     }
3736     [self _updateMouseoverWithFakeEvent];
3737
3738     [self release];
3739 }
3740
3741 - (void)mouseMovedNotification:(NSNotification *)notification
3742 {
3743     [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
3744 }
3745
3746 // returning YES from this method is the way we tell AppKit that it is ok for this view
3747 // to be in the key loop even when "tab to all controls" is not on.
3748 - (BOOL)needsPanelToBecomeKey
3749 {
3750     return YES;
3751 }
3752
3753 // Utility function to make sure we don't return anything through the NSTextInput
3754 // API when an editable region is not currently focused.
3755 static BOOL isTextInput(Frame* coreFrame)
3756 {
3757     return coreFrame && !coreFrame->selection()->isNone() && coreFrame->selection()->isContentEditable();
3758 }
3759
3760 static BOOL isInPasswordField(Frame* coreFrame)
3761 {
3762     return coreFrame && coreFrame->selection()->isInPasswordField();
3763 }
3764
3765 static PassRefPtr<KeyboardEvent> currentKeyboardEvent(Frame* coreFrame)
3766 {
3767     NSEvent *event = [NSApp currentEvent];
3768     if (!event)
3769         return 0;
3770
3771     switch ([event type]) {
3772     case NSKeyDown: {
3773         PlatformKeyboardEvent platformEvent = PlatformEventFactory::createPlatformKeyboardEvent(event);
3774         platformEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown);
3775         return KeyboardEvent::create(platformEvent, coreFrame->document()->defaultView());
3776     }
3777     case NSKeyUp:
3778         return KeyboardEvent::create(PlatformEventFactory::createPlatformKeyboardEvent(event), coreFrame->document()->defaultView());
3779     default:
3780         return 0;
3781     }
3782 }
3783
3784 - (BOOL)becomeFirstResponder
3785 {
3786     NSSelectionDirection direction = NSDirectSelection;
3787     if (![[self _webView] _isPerformingProgrammaticFocus])
3788         direction = [[self window] keyViewSelectionDirection];
3789
3790     [self _updateFontPanel];
3791     
3792     Frame* frame = core([self _frame]);
3793     if (!frame)
3794         return YES;
3795
3796     BOOL exposeInputContext = isTextInput(frame) && !isInPasswordField(frame);
3797     if (exposeInputContext != _private->exposeInputContext) {
3798         _private->exposeInputContext = exposeInputContext;
3799         [NSApp updateWindows];
3800     }
3801
3802     _private->_forceUpdateSecureInputState = YES;
3803     [self _updateSecureInputState];
3804     _private->_forceUpdateSecureInputState = NO;
3805
3806     // FIXME: Kill ring handling is mostly in WebCore, so this call should also be moved there.
3807     frame->editor().setStartNewKillRingSequence(true);
3808
3809     Page* page = frame->page();
3810     if (!page)
3811         return YES;
3812
3813     if (![[self _webView] _isPerformingProgrammaticFocus])
3814         page->focusController()->setFocusedFrame(frame);
3815
3816     page->focusController()->setFocused(true);
3817
3818     if (direction == NSDirectSelection)
3819         return YES;
3820
3821     if (Document* document = frame->document())
3822         document->setFocusedElement(0);
3823     page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward,
3824                                              currentKeyboardEvent(frame).get());
3825     return YES;
3826 }
3827
3828 - (BOOL)resignFirstResponder
3829 {
3830     BOOL resign = [super resignFirstResponder];
3831     if (resign) {
3832         if (_private->isInSecureInputState) {
3833             DisableSecureEventInput();
3834             _private->isInSecureInputState = NO;
3835         }
3836         [_private->completionController endRevertingChange:NO moveLeft:NO];
3837         Frame* coreFrame = core([self _frame]);
3838         if (!coreFrame)
3839             return resign;
3840         Page* page = coreFrame->page();
3841         if (!page)
3842             return resign;
3843         if (![self maintainsInactiveSelection]) { 
3844             [self deselectAll];
3845             if (![[self _webView] _isPerformingProgrammaticFocus])
3846                 [self clearFocus];
3847         }
3848         
3849         id nextResponder = [[self window] _newFirstResponderAfterResigning];
3850         bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
3851             && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
3852         if (!nextResponderIsInWebView)
3853             page->focusController()->setFocused(false);
3854     }
3855     return resign;
3856 }
3857
3858 - (void)setDataSource:(WebDataSource *)dataSource 
3859 {
3860     ASSERT(dataSource);
3861     if (_private->dataSource != dataSource) {
3862         ASSERT(!_private->closed);
3863         BOOL hadDataSource = _private->dataSource != nil;
3864
3865         [dataSource retain];
3866         [_private->dataSource release];
3867         _private->dataSource = dataSource;
3868         [_private->pluginController setDataSource:dataSource];
3869
3870         if (!hadDataSource)
3871             [self addMouseMovedObserver];
3872     }
3873 }
3874
3875 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3876 {
3877 }
3878
3879 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3880 // key.  WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3881 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3882 - (void)updateCell:(NSCell*)cell
3883 {
3884 }
3885
3886 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3887 // pageWidth != 0 implies we will relayout to a new width
3888 - (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
3889 {
3890     if (printing == _private->printing && paginateScreenContent == _private->paginateScreenContent)
3891         return;
3892
3893     WebFrame *frame = [self _frame];
3894     NSArray *subframes = [frame childFrames];
3895     unsigned n = [subframes count];
3896     unsigned i;
3897     for (i = 0; i != n; ++i) {
3898         WebFrame *subframe = [subframes objectAtIndex:i];
3899         WebFrameView *frameView = [subframe frameView];
3900         if ([[subframe _dataSource] _isDocumentHTML]) {
3901             [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:adjustViewSize paginateScreenContent:paginateScreenContent];
3902         }
3903     }
3904
3905     [_private->pageRects release];
3906     _private->pageRects = nil;
3907     _private->printing = printing;
3908     _private->paginateScreenContent = paginateScreenContent;
3909     
3910     Frame* coreFrame = core([self _frame]);
3911     if (coreFrame) {
3912         if (FrameView* coreView = coreFrame->view())
3913             coreView->setMediaType(_private->printing ? "print" : "screen");
3914         if (Document* document = coreFrame->document()) {
3915             // In setting printing, we should not validate resources already cached for the document.
3916             // See https://bugs.webkit.org/show_bug.cgi?id=43704
3917             ResourceCacheValidationSuppressor validationSuppressor(document->cachedResourceLoader());
3918
3919             document->setPaginatedForScreen(_private->paginateScreenContent);
3920             document->setPrinting(_private->printing);
3921             document->styleResolverChanged(RecalcStyleImmediately);
3922         }
3923     }
3924
3925     [self setNeedsLayout:YES];
3926     [self layoutToMinimumPageWidth:minPageLogicalWidth height:minPageLogicalHeight originalPageWidth:originalPageWidth originalPageHeight:originalPageHeight maximumShrinkRatio:maximumShrinkRatio adjustingViewSize:adjustViewSize];
3927     if (!printing) {
3928         // Can't do this when starting printing or nested printing won't work, see 3491427.
3929         [self setNeedsDisplay:NO];
3930     }
3931 }
3932
3933 - (BOOL)canPrintHeadersAndFooters
3934 {
3935     return YES;
3936 }
3937
3938 // This is needed for the case where the webview is embedded in the view that's being printed.
3939 // It shouldn't be called when the webview is being printed directly.
3940 - (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit
3941 {
3942     // This helps when we print as part of a larger print process.
3943     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3944     BOOL wasInPrintingMode = _private->printing;
3945     if (!wasInPrintingMode)
3946         [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
3947
3948     *newBottom = [self _adjustedBottomOfPageWithTop:oldTop bottom:oldBottom limit:bottomLimit];
3949
3950     if (!wasInPrintingMode) {
3951         NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation];
3952         if (currenPrintOperation)
3953             // delay _setPrinting:NO until back to main loop as this method may get called repeatedly
3954             [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0];
3955         else
3956             // not sure if this is actually ever invoked, it probably shouldn't be
3957             [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
3958     }
3959 }
3960
3961 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3962 {
3963     bool useViewWidth = true;
3964     Frame* coreFrame = core([self _frame]);
3965     if (coreFrame) {
3966         Document* document = coreFrame->document();
3967         if (document && document->renderView())
3968             useViewWidth = document->renderView()->style()->isHorizontalWritingMode();
3969     }
3970
3971     float viewLogicalWidth = useViewWidth ? NSWidth([self bounds]) : NSHeight([self bounds]);
3972     if (viewLogicalWidth < 1) {
3973         LOG_ERROR("%@ has no logical width when printing", self);
3974         return 1.0f;
3975     }
3976
3977     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3978     float maxShrinkToFitScaleFactor = 1.0f / _WebHTMLViewPrintingMaximumShrinkFactor;
3979     float shrinkToFitScaleFactor = (useViewWidth ? [printOperation _web_availablePaperWidth] :  [printOperation _web_availablePaperHeight]) / viewLogicalWidth;
3980     return userScaleFactor * max(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor);
3981 }
3982
3983 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3984 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3985 // if AppKit makes it SPI/API.
3986 - (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation 
3987 {
3988     return [self _scaleFactorForPrintOperation:printOperation];
3989 }
3990
3991 // This is used for Carbon printing. At some point we might want to make this public API.
3992 - (void)setPageWidthForPrinting:(float)pageWidth
3993 {
3994     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
3995     [self _setPrinting:YES minimumPageLogicalWidth:pageWidth logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:1 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
3996 }
3997
3998 - (void)_endPrintModeAndRestoreWindowAutodisplay
3999 {
4000     [self _endPrintMode];
4001     [[self window] setAutodisplay:YES];
4002 }
4003
4004 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
4005 {
4006     ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
4007     NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
4008     if (initiatingOperation == currentOperation) {
4009         // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
4010         // being extra paranoid here since the printing code is so fragile. Delay the cleanup
4011         // further.
4012         ASSERT_NOT_REACHED();
4013         [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
4014     } else if ([currentOperation view] == self) {
4015         // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
4016         // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
4017         // fragile. Do nothing, because we don't want to break the print job currently in progress, and