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