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