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