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