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