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