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