Fixed:
[WebKit-https.git] / WebKit / WebView.subproj / WebHTMLView.m
1 /*
2     WebHTMLView.m
3     Copyright 2002, Apple, Inc. All rights reserved.
4 */
5
6 #import <WebKit/WebHTMLView.h>
7
8 #import <WebKit/DOM.h>
9 #import <WebKit/DOMExtensions.h>
10 #import <WebKit/WebArchive.h>
11 #import <WebKit/WebBridge.h>
12 #import <WebKit/WebClipView.h>
13 #import <WebKit/WebDataProtocol.h>
14 #import <WebKit/WebDataSourcePrivate.h>
15 #import <WebKit/WebDocumentInternal.h>
16 #import <WebKit/WebDOMOperationsPrivate.h>
17 #import <WebKit/WebEditingDelegate.h>
18 #import <WebKit/WebException.h>
19 #import <WebKit/WebFramePrivate.h>
20 #import <WebKit/WebFrameViewPrivate.h>
21 #import <WebKit/WebHTMLViewInternal.h>
22 #import <WebKit/WebHTMLRepresentationPrivate.h>
23 #import <WebKit/WebImageRenderer.h>
24 #import <WebKit/WebImageRendererFactory.h>
25 #import <WebKit/WebKitLogging.h>
26 #import <WebKit/WebKitNSStringExtras.h>
27 #import <WebKit/WebNetscapePluginEmbeddedView.h>
28 #import <WebKit/WebNSEventExtras.h>
29 #import <WebKit/WebNSImageExtras.h>
30 #import <WebKit/WebNSObjectExtras.h>
31 #import <WebKit/WebNSPasteboardExtras.h>
32 #import <WebKit/WebNSPrintOperationExtras.h>
33 #import <WebKit/WebNSURLExtras.h>
34 #import <WebKit/WebNSViewExtras.h>
35 #import <WebKit/WebPluginController.h>
36 #import <WebKit/WebPreferences.h>
37 #import <WebKit/WebResourcePrivate.h>
38 #import <WebKit/WebStringTruncator.h>
39 #import <WebKit/WebTextRenderer.h>
40 #import <WebKit/WebTextRendererFactory.h>
41 #import <WebKit/WebUIDelegatePrivate.h>
42 #import <WebKit/WebUnicode.h>
43 #import <WebKit/WebViewInternal.h>
44 #import <WebKit/WebViewPrivate.h>
45
46 #import <AppKit/NSAccessibility.h>
47 #import <AppKit/NSGraphicsContextPrivate.h>
48 #import <AppKit/NSResponder_Private.h>
49
50 #import <Foundation/NSFileManager_NSURLExtras.h>
51 #import <Foundation/NSURL_NSURLExtras.h>
52 #import <Foundation/NSURLFileTypeMappings.h>
53
54 #import <CoreGraphics/CGContextGState.h>
55
56 // Included to help work around this bug:
57 // <rdar://problem/3630640>: "Calling interpretKeyEvents: in a custom text view can fail to process keys right after app startup"
58 #import <AppKit/NSKeyBindingManager.h>
59
60 // By imaging to a width a little wider than the available pixels,
61 // thin pages will be scaled down a little, matching the way they
62 // print in IE and Camino. This lets them use fewer sheets than they
63 // would otherwise, which is presumably why other browsers do this.
64 // Wide pages will be scaled down more than this.
65 #define PrintingMinimumShrinkFactor     1.25
66
67 // This number determines how small we are willing to reduce the page content
68 // in order to accommodate the widest line. If the page would have to be
69 // reduced smaller to make the widest line fit, we just clip instead (this
70 // behavior matches MacIE and Mozilla, at least)
71 #define PrintingMaximumShrinkFactor     2.0
72
73 #define AUTOSCROLL_INTERVAL             0.1
74
75 #define DRAG_LABEL_BORDER_X             4.0
76 #define DRAG_LABEL_BORDER_Y             2.0
77 #define DRAG_LABEL_RADIUS               5.0
78 #define DRAG_LABEL_BORDER_Y_OFFSET              2.0
79
80 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP        120.0
81 #define MAX_DRAG_LABEL_WIDTH                    320.0
82
83 #define DRAG_LINK_LABEL_FONT_SIZE   11.0
84 #define DRAG_LINK_URL_FONT_SIZE   10.0
85
86 static BOOL forceRealHitTest = NO;
87
88 @interface WebHTMLView (WebTextSizing) <_web_WebDocumentTextSizing>
89 @end
90
91 @interface WebHTMLView (WebFileInternal)
92 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
93 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
94 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
95 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
96 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
97 @end
98
99 @interface WebHTMLView (WebHTMLViewPrivate)
100 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize;
101 - (void)_updateTextSizeMultiplier;
102 - (float)_calculatePrintHeight;
103 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation;
104 @end
105
106 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
107 - (void)_updateSelectionForInputManager;
108 @end
109
110 // Any non-zero value will do, but using somethign recognizable might help us debug some day.
111 #define TRACKING_RECT_TAG 0xBADFACE
112
113 @interface NSView (AppKitSecretsIKnowAbout)
114 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
115 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
116 - (NSRect)_dirtyRect;
117 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
118 @end
119
120 @interface NSApplication (AppKitSecretsIKnowAbout)
121 - (void)speakString:(NSString *)string;
122 @end
123
124 @interface NSWindow (AppKitSecretsIKnowAbout)
125 - (id)_newFirstResponderAfterResigning;
126 @end
127
128 @interface NSView (WebHTMLViewPrivate)
129 - (void)_web_setPrintingModeRecursive;
130 - (void)_web_clearPrintingModeRecursive;
131 - (void)_web_layoutIfNeededRecursive:(NSRect)rect testDirtyRect:(bool)testDirtyRect;
132 @end
133
134 @interface NSMutableDictionary (WebHTMLViewPrivate)
135 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
136 @end
137
138 @interface WebElementOrTextFilter : NSObject <DOMNodeFilter>
139 + (WebElementOrTextFilter *)filter;
140 @end
141
142 @interface NSAttributedString(AppKitOnlyOnTiger)
143 - (id)_initWithDOMRange:(DOMRange *)domRange;
144 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
145 @end
146
147 static WebElementOrTextFilter *elementOrTextFilterInstance = nil;
148
149 // Handles the complete: text command
150 @interface WebTextCompleteController : NSObject
151 {
152 @private
153     WebHTMLView *_view;
154     NSWindow *_popupWindow;
155     NSTableView *_tableView;
156     NSArray *_completions;
157     NSString *_originalString;
158     int prefixLength;
159 }
160 - (id)initWithHTMLView:(WebHTMLView *)view;
161 - (void)doCompletion;
162 - (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft;
163 - (BOOL)filterKeyDown:(NSEvent *)event;
164 - (void)_reflectSelection;
165 @end
166
167 @implementation WebHTMLViewPrivate
168
169 - (void)dealloc
170 {
171     ASSERT(autoscrollTimer == nil);
172     ASSERT(autoscrollTriggerEvent == nil);
173     
174     [mouseDownEvent release];
175     [draggingImageURL release];
176     [pluginController release];
177     [toolTip release];
178     [compController release];
179
180     [super dealloc];
181 }
182
183 @end
184
185 @implementation WebHTMLView (WebFileInternal)
186
187 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
188 {
189     NSURLFileTypeMappings *mappings = [NSURLFileTypeMappings sharedMappings];
190     NSArray *imageMIMETypes = [[WebImageRendererFactory sharedFactory] supportedMIMETypes];
191     NSEnumerator *enumerator = [paths objectEnumerator];
192     NSString *path;
193     
194     while ((path = [enumerator nextObject]) != nil) {
195         NSString *MIMEType = [mappings MIMETypeForExtension:[path pathExtension]];
196         if ([imageMIMETypes containsObject:MIMEType]) {
197             return YES;
198         }
199     }
200     
201     return NO;
202 }
203
204 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
205 {
206     DOMDocumentFragment *fragment = [[[self _bridge] DOMDocument] createDocumentFragment];
207     NSURLFileTypeMappings *mappings = [NSURLFileTypeMappings sharedMappings];
208     NSArray *imageMIMETypes = [[WebImageRendererFactory sharedFactory] supportedMIMETypes];
209     NSEnumerator *enumerator = [paths objectEnumerator];
210     WebDataSource *dataSource = [self _dataSource];
211     NSString *path;
212     
213     while ((path = [enumerator nextObject]) != nil) {
214         NSString *MIMEType = [mappings MIMETypeForExtension:[path pathExtension]];
215         if ([imageMIMETypes containsObject:MIMEType]) {
216             WebResource *resource = [[WebResource alloc] initWithData:[NSData dataWithContentsOfFile:path]
217                                                                   URL:[NSURL fileURLWithPath:path]
218                                                              MIMEType:MIMEType 
219                                                      textEncodingName:nil
220                                                             frameName:nil];
221             if (resource) {
222                 [fragment appendChild:[dataSource _imageElementWithImageResource:resource]];
223                 [resource release];
224             }
225         }
226     }
227     
228     return [fragment firstChild] != nil ? fragment : nil;
229 }
230
231 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
232 {
233     NSArray *types = [pasteboard types];
234
235     if ([types containsObject:WebArchivePboardType]) {
236         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
237         if (archive) {
238             DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
239             [archive release];
240             if (fragment) {
241                 return fragment;
242             }
243         }
244     }
245     
246     if ([types containsObject:NSFilenamesPboardType]) {
247         DOMDocumentFragment *fragment = [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
248         if (fragment != nil) {
249             return fragment;
250         }
251     }
252     
253     NSURL *URL;
254     
255     if ([types containsObject:NSHTMLPboardType]) {
256         return [[self _bridge] documentFragmentWithMarkupString:[pasteboard stringForType:NSHTMLPboardType] baseURLString:nil];
257     }
258     
259     if ([types containsObject:NSTIFFPboardType]) {
260         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
261                                                               URL:[NSURL _web_uniqueWebDataURLWithRelativeString:@"/image.tiff"]
262                                                          MIMEType:@"image/tiff" 
263                                                  textEncodingName:nil
264                                                         frameName:nil];
265         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
266         [resource release];
267         return fragment;
268     }
269     
270     if ([types containsObject:NSPICTPboardType]) {
271         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
272                                                               URL:[NSURL _web_uniqueWebDataURLWithRelativeString:@"/image.pict"]
273                                                          MIMEType:@"image/pict" 
274                                                  textEncodingName:nil
275                                                         frameName:nil];
276         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
277         [resource release];
278         return fragment;
279     }
280     
281     if ((URL = [pasteboard _web_bestURL])) {
282         NSString *URLString = [URL _web_originalDataAsString];
283         NSString *linkLabel = [pasteboard stringForType:WebURLNamePboardType];
284         linkLabel = [linkLabel length] > 0 ? linkLabel : URLString;
285
286         DOMDocument *document = [[self _bridge] DOMDocument];
287         DOMDocumentFragment *fragment = [document createDocumentFragment];
288         DOMElement *anchorElement = [document createElement:@"a"];
289         [anchorElement setAttribute:@"href" :URLString];
290         [fragment appendChild:anchorElement];
291         [anchorElement appendChild:[document createTextNode:linkLabel]];
292         return fragment;
293     }
294     
295     NSAttributedString *string = nil;
296     if ([NSAttributedString instancesRespondToSelector:@selector(_documentFromRange:document:documentAttributes:subresources:)]) {
297         if ([types containsObject:NSRTFDPboardType]) {
298             string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
299         }
300         if (string == nil && [types containsObject:NSRTFPboardType]) {
301             string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
302         }
303         if (string != nil) {
304             NSArray *elements = [[NSArray alloc] initWithObjects:@"style", nil];
305             NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:elements, NSExcludedElementsDocumentAttribute, nil];
306             [elements release];
307             NSRange range = NSMakeRange(0, [string length]);
308             NSArray *subresources;
309             DOMDocumentFragment *fragment = [string _documentFromRange:range 
310                                                               document:[[self _bridge] DOMDocument] 
311                                                     documentAttributes:documentAttributes
312                                                           subresources:&subresources];
313             [documentAttributes release];
314             [string release];
315             if (fragment) {
316                 if ([subresources count] != 0) {
317                     [[self _dataSource] _addSubresources:subresources];
318                 }
319                 return fragment;
320             }
321         }
322     }
323     
324     if (allowPlainText && [types containsObject:NSStringPboardType]) {
325         return [[self _bridge] documentFragmentWithText:[pasteboard stringForType:NSStringPboardType]];
326     }
327     
328     return nil;
329 }
330
331 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
332 {
333     DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard allowPlainText:allowPlainText];
334     WebBridge *bridge = [self _bridge];
335     if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[bridge selectedDOMRange] givenAction:WebViewInsertActionPasted]) {
336         [bridge replaceSelectionWithFragment:fragment selectReplacement:NO];
337     }
338 }
339
340 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
341 {
342     WebView *webView = [self _webView];
343     DOMNode *child = [fragment firstChild];
344     if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]]) {
345         return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
346     } else {
347         return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
348     }
349 }
350
351 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
352 {
353     WebView *webView = [self _webView];
354     DOMRange *selectedRange = [[self _bridge] selectedDOMRange];
355
356     return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:selectedRange givenAction:action];
357 }
358
359 @end
360
361 @implementation WebHTMLView (WebPrivate)
362
363 - (void)_reset
364 {
365     [WebImageRenderer stopAnimationsInView:self];
366 }
367
368 - (WebView *)_webView
369 {
370     // We used to use the view hierarchy exclusively here, but that won't work
371     // right when the first viewDidMoveToSuperview call is done, and this wil.
372     return [[self _frame] webView];
373 }
374
375 - (WebFrame *)_frame
376 {
377     WebFrameView *webFrameView = [self _web_parentWebFrameView];
378     return [webFrameView webFrame];
379 }
380
381 // Required so view can access the part's selection.
382 - (WebBridge *)_bridge
383 {
384     return [[self _frame] _bridge];
385 }
386
387 - (WebDataSource *)_dataSource
388 {
389     return [[self _frame] dataSource];
390 }
391
392 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
393 {
394     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
395         location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
396         modifierFlags:[flagsChangedEvent modifierFlags]
397         timestamp:[flagsChangedEvent timestamp]
398         windowNumber:[flagsChangedEvent windowNumber]
399         context:[flagsChangedEvent context]
400         eventNumber:0 clickCount:0 pressure:0];
401
402     // Pretend it's a mouse move.
403     [[NSNotificationCenter defaultCenter]
404         postNotificationName:NSMouseMovedNotification object:self
405         userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
406 }
407
408 - (void)_updateMouseoverWithFakeEvent
409 {
410     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
411         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
412         modifierFlags:[[NSApp currentEvent] modifierFlags]
413         timestamp:[NSDate timeIntervalSinceReferenceDate]
414         windowNumber:[[self window] windowNumber]
415         context:[[NSApp currentEvent] context]
416         eventNumber:0 clickCount:0 pressure:0];
417     
418     [self _updateMouseoverWithEvent:fakeEvent];
419 }
420
421 - (void)_frameOrBoundsChanged
422 {
423     if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
424         [self setNeedsLayout:YES];
425         [self setNeedsDisplay:YES];
426         [_private->compController endRevertingChange:NO moveLeft:NO];
427     }
428
429     NSPoint origin = [[self superview] bounds].origin;
430     if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
431         [[self _bridge] sendScrollEvent];
432         [_private->compController endRevertingChange:NO moveLeft:NO];
433     }
434     _private->lastScrollPosition = origin;
435
436     SEL selector = @selector(_updateMouseoverWithFakeEvent);
437     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:selector object:nil];
438     [self performSelector:selector withObject:nil afterDelay:0];
439 }
440
441 - (void)_setAsideSubviews
442 {
443     ASSERT(!_private->subviewsSetAside);
444     ASSERT(_private->savedSubviews == nil);
445     _private->savedSubviews = _subviews;
446     _subviews = nil;
447     _private->subviewsSetAside = YES;
448  }
449  
450  - (void)_restoreSubviews
451  {
452     ASSERT(_private->subviewsSetAside);
453     ASSERT(_subviews == nil);
454     _subviews = _private->savedSubviews;
455     _private->savedSubviews = nil;
456     _private->subviewsSetAside = NO;
457 }
458
459 // Don't let AppKit even draw subviews. We take care of that.
460 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
461 {
462     // This helps when we print as part of a larger print process.
463     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
464     BOOL wasInPrintingMode = _private->printing;
465     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
466     if (wasInPrintingMode != isPrinting) {
467         if (isPrinting) {
468             [self _web_setPrintingModeRecursive];
469         } else {
470             [self _web_clearPrintingModeRecursive];
471         }
472     }
473
474     [self _web_layoutIfNeededRecursive: rect testDirtyRect:YES];
475
476     [self _setAsideSubviews];
477     [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect
478         rectIsVisibleRectForView:visibleView topView:topView];
479     [self _restoreSubviews];
480
481     if (wasInPrintingMode != isPrinting) {
482         if (wasInPrintingMode) {
483             [self _web_setPrintingModeRecursive];
484         } else {
485             [self _web_clearPrintingModeRecursive];
486         }
487     }
488 }
489
490 // Don't let AppKit even draw subviews. We take care of that.
491 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
492 {
493     BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
494
495     BOOL wasInPrintingMode = _private->printing;
496     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
497
498     if (needToSetAsideSubviews) {
499         // This helps when we print as part of a larger print process.
500         // If the WebHTMLView itself is what we're printing, then we will never have to do this.
501         if (wasInPrintingMode != isPrinting) {
502             if (isPrinting) {
503                 [self _web_setPrintingModeRecursive];
504             } else {
505                 [self _web_clearPrintingModeRecursive];
506             }
507         }
508
509         [self _web_layoutIfNeededRecursive: visRect testDirtyRect:NO];
510
511         [self _setAsideSubviews];
512     }
513     
514     [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
515     
516     if (needToSetAsideSubviews) {
517         if (wasInPrintingMode != isPrinting) {
518             if (wasInPrintingMode) {
519                 [self _web_setPrintingModeRecursive];
520             } else {
521                 [self _web_clearPrintingModeRecursive];
522             }
523         }
524
525         [self _restoreSubviews];
526     }
527 }
528
529 - (BOOL)_insideAnotherHTMLView
530 {
531     NSView *view = self;
532     while ((view = [view superview])) {
533         if ([view isKindOfClass:[WebHTMLView class]]) {
534             return YES;
535         }
536     }
537     return NO;
538 }
539
540 - (void)scrollPoint:(NSPoint)point
541 {
542     // Since we can't subclass NSTextView to do what we want, we have to second guess it here.
543     // If we get called during the handling of a key down event, we assume the call came from
544     // NSTextView, and ignore it and use our own code to decide how to page up and page down
545     // We are smarter about how far to scroll, and we have "superview scrolling" logic.
546     NSEvent *event = [[self window] currentEvent];
547     if ([event type] == NSKeyDown) {
548         const unichar pageUp = NSPageUpFunctionKey;
549         if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageUp length:1]].length == 1) {
550             [self tryToPerform:@selector(scrollPageUp:) with:nil];
551             return;
552         }
553         const unichar pageDown = NSPageDownFunctionKey;
554         if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageDown length:1]].length == 1) {
555             [self tryToPerform:@selector(scrollPageDown:) with:nil];
556             return;
557         }
558     }
559     
560     [super scrollPoint:point];
561 }
562
563 - (NSView *)hitTest:(NSPoint)point
564 {
565     // WebHTMLView objects handle all left mouse clicks for objects inside them.
566     // That does not include left mouse clicks with the control key held down.
567     BOOL captureHitsOnSubviews;
568     if (forceRealHitTest) {
569         captureHitsOnSubviews = NO;
570     } else {
571         NSEvent *event = [[self window] currentEvent];
572         captureHitsOnSubviews = [event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) == 0;
573     }
574     if (!captureHitsOnSubviews) {
575         return [super hitTest:point];
576     }
577     if ([[self superview] mouse:point inRect:[self frame]]) {
578         return self;
579     }
580     return nil;
581 }
582
583 static WebHTMLView *lastHitView = nil;
584
585 - (void)_clearLastHitViewIfSelf
586 {
587     if (lastHitView == self) {
588         lastHitView = nil;
589     }
590 }
591
592 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
593 {
594     ASSERT(_private->trackingRectOwner == nil);
595     _private->trackingRectOwner = owner;
596     _private->trackingRectUserData = data;
597     return TRACKING_RECT_TAG;
598 }
599
600 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
601 {
602     ASSERT(tag == TRACKING_RECT_TAG);
603     return [self addTrackingRect:rect owner:owner userData:data assumeInside:assumeInside];
604 }
605
606 - (void)removeTrackingRect:(NSTrackingRectTag)tag
607 {
608     ASSERT(tag == TRACKING_RECT_TAG);
609     if (_private != nil) {
610         _private->trackingRectOwner = nil;
611     }
612 }
613
614 - (void)_sendToolTipMouseExited
615 {
616     // Nothing matters except window, trackingNumber, and userData.
617     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
618         location:NSMakePoint(0, 0)
619         modifierFlags:0
620         timestamp:0
621         windowNumber:[[self window] windowNumber]
622         context:NULL
623         eventNumber:0
624         trackingNumber:TRACKING_RECT_TAG
625         userData:_private->trackingRectUserData];
626     [_private->trackingRectOwner mouseExited:fakeEvent];
627 }
628
629 - (void)_sendToolTipMouseEntered
630 {
631     // Nothing matters except window, trackingNumber, and userData.
632     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
633         location:NSMakePoint(0, 0)
634         modifierFlags:0
635         timestamp:0
636         windowNumber:[[self window] windowNumber]
637         context:NULL
638         eventNumber:0
639         trackingNumber:TRACKING_RECT_TAG
640         userData:_private->trackingRectUserData];
641     [_private->trackingRectOwner mouseEntered:fakeEvent];
642 }
643
644 - (void)_setToolTip:(NSString *)string
645 {
646     NSString *toolTip = [string length] == 0 ? nil : string;
647     NSString *oldToolTip = _private->toolTip;
648     if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
649         return;
650     }
651     if (oldToolTip) {
652         [self _sendToolTipMouseExited];
653         [oldToolTip release];
654     }
655     _private->toolTip = [toolTip copy];
656     if (toolTip) {
657         [self removeAllToolTips];
658         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
659         [self addToolTipRect:wideOpenRect owner:self userData:NULL];
660         [self _sendToolTipMouseEntered];
661     }
662 }
663
664 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
665 {
666     return [[_private->toolTip copy] autorelease];
667 }
668
669 - (void)_updateMouseoverWithEvent:(NSEvent *)event
670 {
671     WebHTMLView *view = nil;
672     if ([event window] == [self window]) {
673         forceRealHitTest = YES;
674         NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
675         forceRealHitTest = NO;
676         while (hitView) {
677             if ([hitView isKindOfClass:[WebHTMLView class]]) {
678                 view = (WebHTMLView *)hitView;
679                 break;
680             }
681             hitView = [hitView superview];
682         }
683     }
684
685     if (lastHitView != view && lastHitView != nil) {
686         // If we are moving out of a view (or frame), let's pretend the mouse moved
687         // all the way out of that view. But we have to account for scrolling, because
688         // khtml doesn't understand our clipping.
689         NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
690         float yScroll = visibleRect.origin.y;
691         float xScroll = visibleRect.origin.x;
692
693         event = [NSEvent mouseEventWithType:NSMouseMoved
694                          location:NSMakePoint(-1 - xScroll, -1 - yScroll )
695                          modifierFlags:[[NSApp currentEvent] modifierFlags]
696                          timestamp:[NSDate timeIntervalSinceReferenceDate]
697                          windowNumber:[[self window] windowNumber]
698                          context:[[NSApp currentEvent] context]
699                          eventNumber:0 clickCount:0 pressure:0];
700         [[lastHitView _bridge] mouseMoved:event];
701     }
702
703     lastHitView = view;
704     
705     NSDictionary *element;
706     if (view == nil) {
707         element = nil;
708     } else {
709         [[view _bridge] mouseMoved:event];
710
711         NSPoint point = [view convertPoint:[event locationInWindow] fromView:nil];
712         element = [view elementAtPoint:point];
713     }
714
715     // Have the web view send a message to the delegate so it can do status bar display.
716     [[self _webView] _mouseDidMoveOverElement:element modifierFlags:[event modifierFlags]];
717
718     // Set a tool tip; it won't show up right away but will if the user pauses.
719     [self _setToolTip:[element objectForKey:WebCoreElementTitleKey]];
720 }
721
722 + (NSArray *)_insertablePasteboardTypes
723 {
724     static NSArray *types = nil;
725     if (!types) {
726         types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType,
727             NSFilenamesPboardType, NSTIFFPboardType, NSPICTPboardType, NSURLPboardType, 
728             NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
729     }
730     return types;
731 }
732
733 + (NSArray *)_selectionPasteboardTypes
734 {
735     // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
736     return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFPboardType, NSRTFDPboardType, NSStringPboardType, nil];
737 }
738
739 - (WebArchive *)_selectedArchive
740 {
741     NSArray *nodes;
742     NSString *markupString = [[self _bridge] markupStringFromRange:[[self _bridge] selectedDOMRange] nodes:&nodes];
743     return [[self _dataSource] _archiveWithMarkupString:markupString nodes:nodes];
744 }
745
746 - (NSData *)_selectedRTFData
747 {
748     NSAttributedString *attributedString = [self selectedAttributedString];
749     NSRange range = NSMakeRange(0, [attributedString length]);
750     return [attributedString RTFFromRange:range documentAttributes:nil];
751 }
752
753 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
754 {
755     ASSERT([self _hasSelection]);
756     NSArray *types = [[self class] _selectionPasteboardTypes];
757     [pasteboard declareTypes:types owner:nil];
758     [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
759 }
760
761 - (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
762 {
763     NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
764
765     BOOL drawURLString = YES;
766     BOOL clipURLString = NO, clipLabelString = NO;
767     
768     NSString *label = [element objectForKey: WebElementLinkLabelKey];
769     NSString *urlString = [linkURL _web_userVisibleString];
770     
771     if (!label) {
772         drawURLString = NO;
773         label = urlString;
774     }
775     
776     NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
777                                                    toHaveTrait:NSBoldFontMask];
778     NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
779     NSSize labelSize;
780     labelSize.width = [label _web_widthWithFont: labelFont];
781     labelSize.height = [labelFont ascender] - [labelFont descender];
782     if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
783         labelSize.width = MAX_DRAG_LABEL_WIDTH;
784         clipLabelString = YES;
785     }
786     
787     NSSize imageSize, urlStringSize;
788     imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2;
789     imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2;
790     if (drawURLString) {
791         urlStringSize.width = [urlString _web_widthWithFont: urlFont];
792         urlStringSize.height = [urlFont ascender] - [urlFont descender];
793         imageSize.height += urlStringSize.height;
794         if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
795             imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
796             clipURLString = YES;
797         } else {
798             imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2, urlStringSize.width + DRAG_LABEL_BORDER_X * 2);
799         }
800     }
801     NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
802     [dragImage lockFocus];
803     
804     [[NSColor colorWithCalibratedRed: 0.7 green: 0.7 blue: 0.7 alpha: 0.8] set];
805     
806     // Drag a rectangle with rounded corners/
807     NSBezierPath *path = [NSBezierPath bezierPath];
808     [path appendBezierPathWithOvalInRect: NSMakeRect(0,0, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
809     [path appendBezierPathWithOvalInRect: NSMakeRect(0,imageSize.height - DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
810     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2, imageSize.height - DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
811     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2,0, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
812     
813     [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0, imageSize.width - DRAG_LABEL_RADIUS * 2, imageSize.height)];
814     [path appendBezierPathWithRect: NSMakeRect(0, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10, imageSize.height - 2 * DRAG_LABEL_RADIUS)];
815     [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS - 20,DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 20, imageSize.height - 2 * DRAG_LABEL_RADIUS)];
816     [path fill];
817         
818     NSColor *topColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.75];
819     NSColor *bottomColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.5];
820     if (drawURLString) {
821         if (clipURLString)
822             urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2) withFont:urlFont];
823
824         [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender]) 
825              withTopColor:topColor bottomColor:bottomColor font:urlFont];
826     }
827
828     if (clipLabelString)
829         label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2) withFont:labelFont];
830     [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
831              withTopColor:topColor bottomColor:bottomColor font:labelFont];
832     
833     [dragImage unlockFocus];
834     
835     return dragImage;
836 }
837
838 - (BOOL)_startDraggingImage:(NSImage *)wcDragImage at:(NSPoint)wcDragLoc operation:(NSDragOperation)op event:(NSEvent *)mouseDraggedEvent sourceIsDHTML:(BOOL)srcIsDHTML DHTMLWroteData:(BOOL)dhtmlWroteData
839 {
840     NSPoint mouseDownPoint = [self convertPoint:[_private->mouseDownEvent locationInWindow] fromView:nil];
841     NSDictionary *element = [self elementAtPoint:mouseDownPoint];
842
843     NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
844     NSURL *imageURL = [element objectForKey:WebElementImageURLKey];
845     BOOL isSelected = [[element objectForKey:WebElementIsSelectedKey] boolValue];
846
847     [_private->draggingImageURL release];
848     _private->draggingImageURL = nil;
849
850     NSPoint mouseDraggedPoint = [self convertPoint:[mouseDraggedEvent locationInWindow] fromView:nil];
851     _private->webCoreDragOp = op;     // will be DragNone if WebCore doesn't care
852     NSImage *dragImage = nil;
853     NSPoint dragLoc;
854
855     // We allow WebCore to override the drag image, even if its a link, image or text we're dragging.
856     // This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp.
857     // We could verify that ActionDHTML is allowed, although WebCore does claim to respect the action.
858     if (wcDragImage) {
859         dragImage = wcDragImage;
860         // wcDragLoc is the cursor position relative to the lower-left corner of the image.
861         // We add in the Y dimension because we are a flipped view, so adding moves the image down.
862         if (linkURL) {
863             // see HACK below
864             dragLoc = NSMakePoint(mouseDraggedPoint.x - wcDragLoc.x, mouseDraggedPoint.y + wcDragLoc.y);
865         } else {
866             dragLoc = NSMakePoint(mouseDownPoint.x - wcDragLoc.x, mouseDownPoint.y + wcDragLoc.y);
867         }
868         _private->dragOffset = wcDragLoc;
869     }
870     
871     WebView *webView = [self _webView];
872     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
873     WebImageRenderer *image = [element objectForKey:WebElementImageKey];
874     BOOL startedDrag = YES;  // optimism - we almost always manage to start the drag
875
876     // note per kwebster, the offset arg below is always ignored in positioning the image
877     if (imageURL != nil && image != nil && (_private->dragSourceActionMask & WebDragSourceActionImage)) {
878         id source = self;
879         if (!dhtmlWroteData) {
880             _private->draggingImageURL = [imageURL retain];
881             source = [pasteboard _web_declareAndWriteDragImage:image
882                                                            URL:linkURL ? linkURL : imageURL
883                                                          title:[element objectForKey:WebElementImageAltStringKey]
884                                                        archive:[[element objectForKey:WebElementDOMNodeKey] webArchive]
885                                                         source:self];
886         }
887         [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionImage fromPoint:mouseDownPoint withPasteboard:pasteboard];
888         if (dragImage == nil) {
889             [self _web_dragImage:[element objectForKey:WebElementImageKey]
890                             rect:[[element objectForKey:WebElementImageRectKey] rectValue]
891                            event:_private->mouseDownEvent
892                       pasteboard:pasteboard
893                           source:source
894                           offset:&_private->dragOffset];
895         } else {
896             [self dragImage:dragImage
897                          at:dragLoc
898                      offset:NSZeroSize
899                       event:_private->mouseDownEvent
900                  pasteboard:pasteboard
901                      source:source
902                   slideBack:YES];
903         }
904     } else if (linkURL && (_private->dragSourceActionMask & WebDragSourceActionLink)) {
905         if (!dhtmlWroteData) {
906             NSArray *types = [NSPasteboard _web_writableTypesForURL];
907             [pasteboard declareTypes:types owner:self];
908             [pasteboard _web_writeURL:linkURL andTitle:[element objectForKey:WebElementLinkLabelKey] types:types];            
909         }
910         [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionLink fromPoint:mouseDownPoint withPasteboard:pasteboard];
911         if (dragImage == nil) {
912             dragImage = [self _dragImageForLinkElement:element];
913             NSSize offset = NSMakeSize([dragImage size].width / 2, -DRAG_LABEL_BORDER_Y);
914             dragLoc = NSMakePoint(mouseDraggedPoint.x - offset.width, mouseDraggedPoint.y - offset.height);
915             _private->dragOffset.x = offset.width;
916             _private->dragOffset.y = -offset.height;        // inverted because we are flipped
917         }
918         // HACK:  We should pass the mouseDown event instead of the mouseDragged!  This hack gets rid of
919         // a flash of the image at the mouseDown location when the drag starts.
920         [self dragImage:dragImage
921                      at:dragLoc
922                  offset:NSZeroSize
923                   event:mouseDraggedEvent
924              pasteboard:pasteboard
925                  source:self
926               slideBack:YES];
927     } else if (isSelected && (_private->dragSourceActionMask & WebDragSourceActionSelection)) {
928         if (!dhtmlWroteData) {
929             [self _writeSelectionToPasteboard:pasteboard];
930         }
931         [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionSelection fromPoint:mouseDownPoint withPasteboard:pasteboard];
932         if (dragImage == nil) {
933             dragImage = [[self _bridge] selectionImage];
934             [dragImage _web_dissolveToFraction:WebDragImageAlpha];
935             NSRect visibleSelectionRect = [[self _bridge] visibleSelectionRect];
936             dragLoc = NSMakePoint(NSMinX(visibleSelectionRect), NSMaxY(visibleSelectionRect));
937             _private->dragOffset.x = mouseDownPoint.x - dragLoc.x;
938             _private->dragOffset.y = dragLoc.y - mouseDownPoint.y;        // inverted because we are flipped
939         }
940         [self dragImage:dragImage
941                      at:dragLoc
942                  offset:NSZeroSize
943                   event:_private->mouseDownEvent
944              pasteboard:pasteboard
945                  source:self
946               slideBack:YES];
947     } else if (srcIsDHTML) {
948         ASSERT(_private->dragSourceActionMask & WebDragSourceActionDHTML);
949         [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionDHTML fromPoint:mouseDownPoint withPasteboard:pasteboard];
950         if (dragImage == nil) {
951             // WebCore should have given us an image, but we'll make one up
952             NSString *imagePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"missing_image" ofType:@"tiff"];
953             dragImage = [[[NSImage alloc] initWithContentsOfFile:imagePath] autorelease];
954             NSSize imageSize = [dragImage size];
955             dragLoc = NSMakePoint(mouseDownPoint.x - imageSize.width/2, mouseDownPoint.y + imageSize.height/2);
956             _private->dragOffset.x = imageSize.width/2;
957             _private->dragOffset.y = imageSize.height/2;        // inverted because we are flipped
958         }
959         [self dragImage:dragImage
960                      at:dragLoc
961                  offset:NSZeroSize
962                   event:_private->mouseDownEvent
963              pasteboard:pasteboard
964                  source:self
965               slideBack:YES];
966     } else {
967         // Only way I know if to get here is if the original element clicked on in the mousedown is no longer
968         // under the mousedown point, so linkURL, imageURL and isSelected are all false/nil.
969         startedDrag = NO;
970     }
971     return startedDrag;
972 }
973
974 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
975 {
976     [self autoscroll:event];
977     [self _startAutoscrollTimer:event];
978 }
979
980 - (BOOL)_mayStartDragAtEventLocation:(NSPoint)location
981 {
982     NSPoint mouseDownPoint = [self convertPoint:location fromView:nil];
983     NSDictionary *mouseDownElement = [self elementAtPoint:mouseDownPoint];
984
985     if ([mouseDownElement objectForKey: WebElementImageKey] != nil &&
986         [mouseDownElement objectForKey: WebElementImageURLKey] != nil && 
987         [[WebPreferences standardPreferences] loadsImagesAutomatically] && 
988         (_private->dragSourceActionMask & WebDragSourceActionImage)) {
989         return YES;
990     }
991     
992     if ([mouseDownElement objectForKey:WebElementLinkURLKey] != nil && 
993         (_private->dragSourceActionMask & WebDragSourceActionLink)) {
994         return YES;
995     }
996     
997     if ([[mouseDownElement objectForKey:WebElementIsSelectedKey] boolValue] &&
998         (_private->dragSourceActionMask & WebDragSourceActionSelection)) {
999         return YES;
1000     }
1001     
1002     return NO;
1003 }
1004
1005 - (WebPluginController *)_pluginController
1006 {
1007     return _private->pluginController;
1008 }
1009
1010 - (void)_web_setPrintingModeRecursive
1011 {
1012     [self _setPrinting:YES minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
1013     [super _web_setPrintingModeRecursive];
1014 }
1015
1016 - (void)_web_clearPrintingModeRecursive
1017 {
1018     [self _setPrinting:NO minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
1019     [super _web_clearPrintingModeRecursive];
1020 }
1021
1022 - (void)_web_layoutIfNeededRecursive:(NSRect)displayRect testDirtyRect:(bool)testDirtyRect
1023 {
1024     ASSERT(!_private->subviewsSetAside);
1025     displayRect = NSIntersectionRect(displayRect, [self bounds]);
1026
1027     if (!testDirtyRect || [self needsDisplay]) {
1028         if (testDirtyRect) {
1029             NSRect dirtyRect = [self _dirtyRect];
1030             displayRect = NSIntersectionRect(displayRect, dirtyRect);
1031         }
1032         if (!NSIsEmptyRect(displayRect)) {
1033             if ([[self _bridge] needsLayout])
1034                 _private->needsLayout = YES;
1035             if (_private->needsToApplyStyles || _private->needsLayout)
1036                 [self layout];
1037         }
1038     }
1039
1040     [super _web_layoutIfNeededRecursive: displayRect testDirtyRect: NO];
1041 }
1042
1043 - (NSRect)_selectionRect
1044 {
1045     return [[self _bridge] selectionRect];
1046 }
1047
1048 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1049 {
1050     if (_private->autoscrollTimer == nil) {
1051         _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1052             target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1053         _private->autoscrollTriggerEvent = [triggerEvent retain];
1054     }
1055 }
1056
1057 - (void)_stopAutoscrollTimer
1058 {
1059     NSTimer *timer = _private->autoscrollTimer;
1060     _private->autoscrollTimer = nil;
1061     [_private->autoscrollTriggerEvent release];
1062     _private->autoscrollTriggerEvent = nil;
1063     [timer invalidate];
1064     [timer release];
1065 }
1066
1067 - (void)_autoscroll
1068 {
1069     int isStillDown;
1070     
1071     // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1072     // a mouse up event.
1073     PSstilldown([_private->autoscrollTriggerEvent eventNumber], &isStillDown);
1074     if (!isStillDown){
1075         [self _stopAutoscrollTimer];
1076         return;
1077     }
1078
1079     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1080         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1081         modifierFlags:[[NSApp currentEvent] modifierFlags]
1082         timestamp:[NSDate timeIntervalSinceReferenceDate]
1083         windowNumber:[[self window] windowNumber]
1084         context:[[NSApp currentEvent] context]
1085         eventNumber:0 clickCount:0 pressure:0];
1086     [self mouseDragged:fakeEvent];
1087 }
1088
1089 - (BOOL)_canCopy
1090 {
1091     // Copying can be done regardless of whether you can edit.
1092     return [self _hasSelection];
1093 }
1094
1095 - (BOOL)_canCut
1096 {
1097     return [self _hasSelection] && [self _isEditable];
1098 }
1099
1100 - (BOOL)_canDelete
1101 {
1102     return [self _hasSelection] && [self _isEditable];
1103 }
1104
1105 - (BOOL)_canPaste
1106 {
1107     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1108 }
1109
1110 - (BOOL)_canType
1111 {
1112     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1113 }
1114
1115 - (BOOL)_hasSelection
1116 {
1117     return [[self _bridge] selectionState] == WebSelectionStateRange;
1118 }
1119
1120 - (BOOL)_hasSelectionOrInsertionPoint
1121 {
1122     return [[self _bridge] selectionState] != WebSelectionStateNone;
1123 }
1124
1125 - (BOOL)_isEditable
1126 {
1127     return [[self _webView] isEditable] || [[self _bridge] isSelectionEditable];
1128 }
1129
1130 @end
1131
1132 @implementation NSView (WebHTMLViewPrivate)
1133
1134 - (void)_web_setPrintingModeRecursive
1135 {
1136     [_subviews makeObjectsPerformSelector:@selector(_web_setPrintingModeRecursive)];
1137 }
1138
1139 - (void)_web_clearPrintingModeRecursive
1140 {
1141     [_subviews makeObjectsPerformSelector:@selector(_web_clearPrintingModeRecursive)];
1142 }
1143
1144 - (void)_web_layoutIfNeededRecursive: (NSRect)rect testDirtyRect:(bool)testDirtyRect
1145 {
1146     unsigned index, count;
1147     for (index = 0, count = [_subviews count]; index < count; index++) {
1148         NSView *subview = [_subviews objectAtIndex:index];
1149         NSRect dirtiedSubviewRect = [subview convertRect: rect fromView: self];
1150         [subview _web_layoutIfNeededRecursive: dirtiedSubviewRect testDirtyRect:testDirtyRect];
1151     }
1152 }
1153
1154 @end
1155
1156 @implementation NSMutableDictionary (WebHTMLViewPrivate)
1157
1158 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
1159 {
1160     if (object == nil) {
1161         [self removeObjectForKey:key];
1162     } else {
1163         [self setObject:object forKey:key];
1164     }
1165 }
1166
1167 @end
1168
1169 // The following is a workaround for
1170 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
1171 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
1172 // Since the category will be searched before the real class, we'll prevent the flag from being
1173 // set on the tool tip panel.
1174
1175 @interface NSToolTipPanel : NSPanel
1176 @end
1177
1178 @interface NSToolTipPanel (WebHTMLViewPrivate)
1179 @end
1180
1181 @implementation NSToolTipPanel (WebHTMLViewPrivate)
1182
1183 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
1184 {
1185     // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
1186 }
1187
1188 @end
1189
1190
1191 @interface WebHTMLView (TextSizing) <_web_WebDocumentTextSizing>
1192 @end
1193
1194 @interface NSArray (WebHTMLView)
1195 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
1196 @end
1197
1198 @implementation WebHTMLView
1199
1200 + (void)initialize
1201 {
1202     WebKitInitializeUnicode();
1203     [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] returnTypes:nil];
1204 }
1205
1206 - (id)initWithFrame:(NSRect)frame
1207 {
1208     [super initWithFrame:frame];
1209     
1210     // Make all drawing go through us instead of subviews.
1211     // The bulk of the code to handle this is in WebHTMLViewPrivate.m.
1212     if (NSAppKitVersionNumber >= 711) {
1213         [self _setDrawsOwnDescendants:YES];
1214     }
1215     
1216     _private = [[WebHTMLViewPrivate alloc] init];
1217
1218     _private->pluginController = [[WebPluginController alloc] initWithHTMLView:self];
1219     _private->needsLayout = YES;
1220
1221     return self;
1222 }
1223
1224 - (void)dealloc
1225 {
1226     [self _clearLastHitViewIfSelf];
1227     [self _reset];
1228     [[NSNotificationCenter defaultCenter] removeObserver:self];
1229     [_private->pluginController destroyAllPlugins];
1230     [_private release];
1231     _private = nil;
1232     [super dealloc];
1233 }
1234
1235 - (void)finalize
1236 {
1237     [self _clearLastHitViewIfSelf];
1238     [self _reset];
1239     [[NSNotificationCenter defaultCenter] removeObserver:self];
1240     [_private->pluginController destroyAllPlugins];
1241     _private = nil;
1242     [super finalize];
1243 }
1244
1245 - (IBAction)takeFindStringFromSelection:(id)sender
1246 {
1247     if (![self _hasSelection]) {
1248         NSBeep();
1249         return;
1250     }
1251
1252     [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
1253 }
1254
1255 - (NSArray *)pasteboardTypesForSelection
1256 {
1257     return [[self class] _selectionPasteboardTypes];
1258 }
1259
1260 - (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
1261 {
1262     // Put HTML on the pasteboard.
1263     if ([types containsObject:WebArchivePboardType]) {
1264         WebArchive *archive = [self _selectedArchive];
1265         [pasteboard setData:[archive data] forType:WebArchivePboardType];
1266     }
1267     
1268     // Put attributed string on the pasteboard (RTF format).
1269     NSData *RTFData = nil;
1270     if ([types containsObject:NSRTFPboardType]) {
1271         RTFData = [self _selectedRTFData];
1272         [pasteboard setData:RTFData forType:NSRTFPboardType];
1273     }
1274     if ([types containsObject:NSRTFDPboardType]) {
1275         if (!RTFData) {
1276             RTFData = [self _selectedRTFData];
1277         }
1278         [pasteboard setData:RTFData forType:NSRTFDPboardType];
1279     }
1280     
1281     // Put plain string on the pasteboard.
1282     if ([types containsObject:NSStringPboardType]) {
1283         // Map &nbsp; to a plain old space because this is better for source code, other browsers do it,
1284         // and because HTML forces you to do this any time you want two spaces in a row.
1285         NSMutableString *s = [[self selectedString] mutableCopy];
1286         const unichar NonBreakingSpaceCharacter = 0xA0;
1287         NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
1288         [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
1289         [pasteboard setString:s forType:NSStringPboardType];
1290         [s release];
1291     }
1292 }
1293
1294 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
1295 {
1296     [self _writeSelectionToPasteboard:pasteboard];
1297     return YES;
1298 }
1299
1300 - (void)selectAll:(id)sender
1301 {
1302     [self selectAll];
1303 }
1304
1305 - (void)jumpToSelection: sender
1306 {
1307     [[self _bridge] jumpToSelection];
1308 }
1309
1310 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item 
1311 {
1312     SEL action = [item action];
1313     WebBridge *bridge = [self _bridge];
1314
1315     if (action == @selector(cut:)) {
1316         return [bridge mayDHTMLCut] || [self _canDelete];
1317     } else if (action == @selector(copy:)) {
1318         return [bridge mayDHTMLCopy] || [self _canCopy];
1319     } else if (action == @selector(delete:)) {
1320         return [self _canDelete];
1321     } else if (action == @selector(paste:)) {
1322         return [bridge mayDHTMLPaste] || [self _canPaste];
1323     } else if (action == @selector(takeFindStringFromSelection:)) {
1324         return [self _hasSelection];
1325     } else if (action == @selector(jumpToSelection:)) {
1326         return [self _hasSelection];
1327     } else if (action == @selector(checkSpelling:)
1328                || action == @selector(showGuessPanel:)
1329                || action == @selector(changeSpelling:)
1330                || action == @selector(ignoreSpelling:)) {
1331         return [[self _bridge] isSelectionEditable];
1332     }
1333
1334     return YES;
1335 }
1336
1337 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
1338 {
1339     if (sendType && ([[[self class] _selectionPasteboardTypes] containsObject:sendType]) && [self _hasSelection]){
1340         return self;
1341     }
1342
1343     return [super validRequestorForSendType:sendType returnType:returnType];
1344 }
1345
1346 - (BOOL)acceptsFirstResponder
1347 {
1348     // Don't accept first responder when we first click on this view.
1349     // We have to pass the event down through WebCore first to be sure we don't hit a subview.
1350     // Do accept first responder at any other time, for example from keyboard events,
1351     // or from calls back from WebCore once we begin mouse-down event handling.
1352     NSEvent *event = [NSApp currentEvent];
1353     if ([event type] == NSLeftMouseDown && event != _private->mouseDownEvent && 
1354         NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
1355         return NO;
1356     }
1357     return YES;
1358 }
1359
1360 - (BOOL)maintainsInactiveSelection
1361 {
1362     // This method helps to determing whether the view should maintain
1363     // an inactive selection when the view is not first responder.
1364     // Traditionally, these views have not maintained such selections,
1365     // clearing them when the view was not first responder. However,
1366     // to fix bugs like this one:
1367     // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even 
1368     //                            when they're not firstResponder"
1369     // it was decided to add a switch to act more like an NSTextView.
1370     // For now, however, the view only acts in this way when the
1371     // web view is set to be editable. This will maintain traditional
1372     // behavior for WebKit clients dating back to before this change,
1373     // and will likely be a decent switch for the long term, since
1374     // clients to ste the web view to be editable probably want it
1375     // to act like a "regular" Cocoa view in terms of its selection
1376     // behavior.
1377     if (![[self _webView] isEditable])
1378         return NO;
1379         
1380     id nextResponder = [[self window] _newFirstResponderAfterResigning];
1381     return !nextResponder || ![nextResponder isKindOfClass:[NSView class]] || ![nextResponder isDescendantOf:[self _webView]];
1382 }
1383
1384 - (void)addMouseMovedObserver
1385 {
1386     if ([[self window] isKeyWindow] && ![self _insideAnotherHTMLView]) {
1387         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
1388             name:NSMouseMovedNotification object:nil];
1389         [self _frameOrBoundsChanged];
1390     }
1391 }
1392
1393 - (void)removeMouseMovedObserver
1394 {
1395     [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
1396     [[NSNotificationCenter defaultCenter] removeObserver:self
1397         name:NSMouseMovedNotification object:nil];
1398 }
1399
1400 - (void)updateFocusDisplay
1401 {
1402     // This method does the job of updating the view based on the view's firstResponder-ness and
1403     // the window key-ness of the window containing this view. This involves three kinds of 
1404     // drawing updates right now, all handled in WebCore in response to the call over the bridge. 
1405     // 
1406     // The three display attributes are as follows:
1407     // 
1408     // 1. The background color used to draw behind selected content (active | inactive color)
1409     // 2. Caret blinking (blinks | does not blink)
1410     // 3. The drawing of a focus ring around links in web pages.
1411
1412     BOOL flag = !_private->resigningFirstResponder && [[self window] isKeyWindow] && [self _web_firstResponderCausesFocusDisplay];
1413     [[self _bridge] setDisplaysWithFocusAttributes:flag];
1414 }
1415
1416 - (void)addSuperviewObservers
1417 {
1418     // We watch the bounds of our superview, so that we can do a layout when the size
1419     // of the superview changes. This is different from other scrollable things that don't
1420     // need this kind of thing because their layout doesn't change.
1421     
1422     // We need to pay attention to both height and width because our "layout" has to change
1423     // to extend the background the full height of the space and because some elements have
1424     // sizes that are based on the total size of the view.
1425     
1426     NSView *superview = [self superview];
1427     if (superview && [self window]) {
1428         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
1429             name:NSViewFrameDidChangeNotification object:superview];
1430         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
1431             name:NSViewBoundsDidChangeNotification object:superview];
1432     }
1433 }
1434
1435 - (void)removeSuperviewObservers
1436 {
1437     NSView *superview = [self superview];
1438     if (superview && [self window]) {
1439         [[NSNotificationCenter defaultCenter] removeObserver:self
1440             name:NSViewFrameDidChangeNotification object:superview];
1441         [[NSNotificationCenter defaultCenter] removeObserver:self
1442             name:NSViewBoundsDidChangeNotification object:superview];
1443     }
1444 }
1445
1446 - (void)addWindowObservers
1447 {
1448     NSWindow *window = [self window];
1449     if (window) {
1450         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:)
1451             name:NSWindowDidBecomeKeyNotification object:window];
1452         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:)
1453             name:NSWindowDidResignKeyNotification object:window];
1454         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:)
1455             name:NSWindowWillCloseNotification object:window];
1456     }
1457 }
1458
1459 - (void)removeWindowObservers
1460 {
1461     NSWindow *window = [self window];
1462     if (window) {
1463         [[NSNotificationCenter defaultCenter] removeObserver:self
1464             name:NSWindowDidBecomeKeyNotification object:window];
1465         [[NSNotificationCenter defaultCenter] removeObserver:self
1466             name:NSWindowDidResignKeyNotification object:window];
1467         [[NSNotificationCenter defaultCenter] removeObserver:self
1468             name:NSWindowWillCloseNotification object:window];
1469     }
1470 }
1471
1472 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
1473 {
1474     [self removeSuperviewObservers];
1475 }
1476
1477 - (void)viewDidMoveToSuperview
1478 {
1479     // Do this here in case the text size multiplier changed when a non-HTML
1480     // view was installed.
1481     if ([self superview] != nil) {
1482         [self _updateTextSizeMultiplier];
1483         [self addSuperviewObservers];
1484     }
1485 }
1486
1487 - (void)viewWillMoveToWindow:(NSWindow *)window
1488 {
1489     // Don't do anything if we aren't initialized.  This happens
1490     // when decoding a WebView.  When WebViews are decoded their subviews
1491     // are created by initWithCoder: and so won't be normally
1492     // initialized.  The stub views are discarded by WebView.
1493     if (_private){
1494         // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
1495         [self removeMouseMovedObserver];
1496         [self removeWindowObservers];
1497         [self removeSuperviewObservers];
1498         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
1499     
1500         [[self _pluginController] stopAllPlugins];
1501     }
1502 }
1503
1504 - (void)viewDidMoveToWindow
1505 {
1506     // Don't do anything if we aren't initialized.  This happens
1507     // when decoding a WebView.  When WebViews are decoded their subviews
1508     // are created by initWithCoder: and so won't be normally
1509     // initialized.  The stub views are discarded by WebView.
1510     if (_private) {
1511         [self _stopAutoscrollTimer];
1512         if ([self window]) {
1513             _private->lastScrollPosition = [[self superview] bounds].origin;
1514             [self addWindowObservers];
1515             [self addSuperviewObservers];
1516             [self addMouseMovedObserver];
1517             // Schedule this update, rather than making the call right now.
1518             // The reason is that placing the caret in the just-installed view requires
1519             // the HTML/XML document to be available on the WebCore side, but it is not
1520             // at the time this code is running. However, it will be there on the next
1521             // crank of the run loop. Doing this helps to make a blinking caret appear 
1522             // in a new, empty window "automatic".
1523             [self performSelector:@selector(updateFocusDisplay) withObject:nil afterDelay:0];
1524     
1525             [[self _pluginController] startAllPlugins];
1526     
1527             _private->lastScrollPosition = NSZeroPoint;
1528             
1529             _private->inWindow = YES;
1530         } else {
1531             // Reset when we are moved out of a window after being moved into one.
1532             // Without this check, we reset ourselves before we even start.
1533             // This is only needed because viewDidMoveToWindow is called even when
1534             // the window is not changing (bug in AppKit).
1535             if (_private->inWindow) {
1536                 [self _reset];
1537                 _private->inWindow = NO;
1538             }
1539         }
1540     }
1541 }
1542
1543 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1544 {
1545     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
1546 }
1547
1548 - (void)viewDidMoveToHostWindow
1549 {
1550     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
1551 }
1552
1553
1554 - (void)addSubview:(NSView *)view
1555 {
1556     [super addSubview:view];
1557
1558     if ([[view class] respondsToSelector:@selector(plugInViewWithArguments:)] || [view respondsToSelector:@selector(pluginInitialize)] || [view respondsToSelector:@selector(webPlugInInitialize)]) {
1559         [[self _pluginController] addPlugin:view];
1560     }
1561 }
1562
1563 - (void)reapplyStyles
1564 {
1565     if (!_private->needsToApplyStyles) {
1566         return;
1567     }
1568     
1569 #ifdef _KWQ_TIMING        
1570     double start = CFAbsoluteTimeGetCurrent();
1571 #endif
1572
1573     [[self _bridge] reapplyStylesForDeviceType:
1574         _private->printing ? WebCoreDevicePrinter : WebCoreDeviceScreen];
1575     
1576 #ifdef _KWQ_TIMING        
1577     double thisTime = CFAbsoluteTimeGetCurrent() - start;
1578     LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
1579 #endif
1580
1581     _private->needsToApplyStyles = NO;
1582 }
1583
1584 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
1585 // minPageWidth==0 implies a non-printing layout
1586 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
1587 {
1588     [self reapplyStyles];
1589     
1590     // Ensure that we will receive mouse move events.  Is this the best place to put this?
1591     [[self window] setAcceptsMouseMovedEvents: YES];
1592     [[self window] _setShouldPostEventNotifications: YES];
1593
1594     if (!_private->needsLayout) {
1595         return;
1596     }
1597
1598 #ifdef _KWQ_TIMING        
1599     double start = CFAbsoluteTimeGetCurrent();
1600 #endif
1601
1602     LOG(View, "%@ doing layout", self);
1603
1604     if (minPageWidth > 0.0) {
1605         [[self _bridge] forceLayoutWithMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
1606     } else {
1607         [[self _bridge] forceLayoutAdjustingViewSize:adjustViewSize];
1608     }
1609     _private->needsLayout = NO;
1610     
1611     if (!_private->printing) {
1612         // get size of the containing dynamic scrollview, so
1613         // appearance and disappearance of scrollbars will not show up
1614         // as a size change
1615         NSSize newLayoutFrameSize = [[[self superview] superview] frame].size;
1616         if (_private->laidOutAtLeastOnce && !NSEqualSizes(_private->lastLayoutFrameSize, newLayoutFrameSize)) {
1617             [[self _bridge] sendResizeEvent];
1618         }
1619         _private->laidOutAtLeastOnce = YES;
1620         _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
1621         _private->lastLayoutFrameSize = newLayoutFrameSize;
1622     }
1623
1624 #ifdef _KWQ_TIMING        
1625     double thisTime = CFAbsoluteTimeGetCurrent() - start;
1626     LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
1627 #endif
1628 }
1629
1630 - (void)layout
1631 {
1632     [self layoutToMinimumPageWidth:0.0 maximumPageWidth:0.0 adjustingViewSize:NO];
1633 }
1634
1635 - (NSMenu *)menuForEvent:(NSEvent *)event
1636 {
1637     [_private->compController endRevertingChange:NO moveLeft:NO];
1638
1639     if ([[self _bridge] sendContextMenuEvent:event]) {
1640         return nil;
1641     }
1642     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
1643     NSDictionary *element = [self elementAtPoint:point];
1644     return [[self _webView] _menuForElement:element];
1645 }
1646
1647 // Search from the end of the currently selected location, or from the beginning of the
1648 // document if nothing is selected.
1649 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag;
1650 {
1651     return [[self _bridge] searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag];
1652 }
1653
1654 - (NSString *)string
1655 {
1656     // FIXME: We should be using WebCore logic for converting the document into a string and not creating an attributed string to do this.
1657     return [[self attributedString] string];
1658 }
1659
1660 - (NSAttributedString *)_attributeStringFromDOMRange:(DOMRange *)range
1661 {
1662     NSAttributedString *attributedString = nil;
1663     if ([NSAttributedString instancesRespondToSelector:@selector(_initWithDOMRange:)]) {
1664         attributedString = [[[NSAttributedString alloc] _initWithDOMRange:range] autorelease];
1665     }
1666     return attributedString;
1667 }
1668
1669 - (NSAttributedString *)attributedString
1670 {
1671     WebBridge *bridge = [self _bridge];
1672     DOMDocument *document = [bridge DOMDocument];
1673     NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[document _documentRange]];
1674     if (attributedString == nil) {
1675         attributedString = [bridge attributedStringFrom:document startOffset:0 to:nil endOffset:0];
1676     }
1677     return attributedString;
1678 }
1679
1680 - (NSString *)selectedString
1681 {
1682     return [[self _bridge] selectedString];
1683 }
1684
1685 - (NSAttributedString *)selectedAttributedString
1686 {
1687     WebBridge *bridge = [self _bridge];
1688     NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[bridge selectedDOMRange]];
1689     if (attributedString == nil) {
1690         attributedString = [bridge selectedAttributedString];
1691     }
1692     return attributedString;
1693 }
1694
1695 - (void)selectAll
1696 {
1697     [[self _bridge] selectAll];
1698 }
1699
1700 // Remove the selection.
1701 - (void)deselectAll
1702 {
1703     [[self _bridge] deselectAll];
1704 }
1705
1706 - (void)deselectText
1707 {
1708     [[self _bridge] deselectText];
1709 }
1710
1711 - (BOOL)isOpaque
1712 {
1713     return [[self _webView] drawsBackground];
1714 }
1715
1716 - (void)setNeedsDisplay:(BOOL)flag
1717 {
1718     LOG(View, "%@ flag = %d", self, (int)flag);
1719     [super setNeedsDisplay: flag];
1720 }
1721
1722 - (void)setNeedsLayout: (BOOL)flag
1723 {
1724     LOG(View, "%@ flag = %d", self, (int)flag);
1725     _private->needsLayout = flag;
1726 }
1727
1728
1729 - (void)setNeedsToApplyStyles: (BOOL)flag
1730 {
1731     LOG(View, "%@ flag = %d", self, (int)flag);
1732     _private->needsToApplyStyles = flag;
1733 }
1734
1735 - (void)drawRect:(NSRect)rect
1736 {
1737     LOG(View, "%@ drawing", self);
1738     
1739     BOOL subviewsWereSetAside = _private->subviewsSetAside;
1740     if (subviewsWereSetAside) {
1741         [self _restoreSubviews];
1742     }
1743
1744 #ifdef _KWQ_TIMING
1745     double start = CFAbsoluteTimeGetCurrent();
1746 #endif
1747
1748     [NSGraphicsContext saveGraphicsState];
1749     NSRectClip(rect);
1750         
1751     ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
1752
1753     [(WebClipView *)[self superview] setAdditionalClip:rect];
1754
1755     NS_DURING {
1756         WebTextRendererFactory *textRendererFactoryIfCoalescing = nil;
1757         if ([WebTextRenderer shouldBufferTextDrawing] && [NSView focusView]) {
1758             textRendererFactoryIfCoalescing = [WebTextRendererFactory sharedFactory];
1759             [textRendererFactoryIfCoalescing startCoalesceTextDrawing];
1760         }
1761
1762         if (![[self _webView] drawsBackground]) {
1763             [[NSColor clearColor] set];
1764             NSRectFill (rect);
1765         }
1766         
1767         //double start = CFAbsoluteTimeGetCurrent();
1768         [[self _bridge] drawRect:rect];
1769         //LOG(Timing, "draw time %e", CFAbsoluteTimeGetCurrent() - start);
1770
1771         if (textRendererFactoryIfCoalescing != nil) {
1772             [textRendererFactoryIfCoalescing endCoalesceTextDrawing];
1773         }
1774
1775         [(WebClipView *)[self superview] resetAdditionalClip];
1776
1777         [NSGraphicsContext restoreGraphicsState];
1778     } NS_HANDLER {
1779         [(WebClipView *)[self superview] resetAdditionalClip];
1780         [NSGraphicsContext restoreGraphicsState];
1781         ERROR("Exception caught while drawing: %@", localException);
1782         [localException raise];
1783     } NS_ENDHANDLER
1784
1785 #ifdef DEBUG_LAYOUT
1786     NSRect vframe = [self frame];
1787     [[NSColor blackColor] set];
1788     NSBezierPath *path;
1789     path = [NSBezierPath bezierPath];
1790     [path setLineWidth:(float)0.1];
1791     [path moveToPoint:NSMakePoint(0, 0)];
1792     [path lineToPoint:NSMakePoint(vframe.size.width, vframe.size.height)];
1793     [path closePath];
1794     [path stroke];
1795     path = [NSBezierPath bezierPath];
1796     [path setLineWidth:(float)0.1];
1797     [path moveToPoint:NSMakePoint(0, vframe.size.height)];
1798     [path lineToPoint:NSMakePoint(vframe.size.width, 0)];
1799     [path closePath];
1800     [path stroke];
1801 #endif
1802
1803 #ifdef _KWQ_TIMING
1804     double thisTime = CFAbsoluteTimeGetCurrent() - start;
1805     LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
1806 #endif
1807
1808     if (subviewsWereSetAside) {
1809         [self _setAsideSubviews];
1810     }
1811 }
1812
1813 // Turn off the additional clip while computing our visibleRect.
1814 - (NSRect)visibleRect
1815 {
1816     if (!([[self superview] isKindOfClass:[WebClipView class]]))
1817         return [super visibleRect];
1818         
1819     WebClipView *clipView = (WebClipView *)[self superview];
1820
1821     BOOL hasAdditionalClip = [clipView hasAdditionalClip];
1822     if (!hasAdditionalClip) {
1823         return [super visibleRect];
1824     }
1825     
1826     NSRect additionalClip = [clipView additionalClip];
1827     [clipView resetAdditionalClip];
1828     NSRect visibleRect = [super visibleRect];
1829     [clipView setAdditionalClip:additionalClip];
1830     return visibleRect;
1831 }
1832
1833 - (BOOL)isFlipped 
1834 {
1835     return YES;
1836 }
1837
1838 - (void)windowDidBecomeKey:(NSNotification *)notification
1839 {
1840     ASSERT([notification object] == [self window]);
1841     [self addMouseMovedObserver];
1842     [self updateFocusDisplay];
1843 }
1844
1845 - (void)windowDidResignKey: (NSNotification *)notification
1846 {
1847     ASSERT([notification object] == [self window]);
1848     [_private->compController endRevertingChange:NO moveLeft:NO];
1849     [self removeMouseMovedObserver];
1850     [self updateFocusDisplay];
1851 }
1852
1853 - (void)windowWillClose:(NSNotification *)notification
1854 {
1855     [_private->compController endRevertingChange:NO moveLeft:NO];
1856     [[self _pluginController] destroyAllPlugins];
1857 }
1858
1859 - (BOOL)_isSelectionEvent:(NSEvent *)event
1860 {
1861     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
1862     return [[[self elementAtPoint:point] objectForKey:WebElementIsSelectedKey] boolValue];
1863 }
1864
1865 - (BOOL)acceptsFirstMouse:(NSEvent *)event
1866 {
1867     // We hack AK's hitTest method to catch all events at the topmost WebHTMLView.  However, for
1868     // the purposes of this method we want to really query the deepest view, so we forward to it.
1869     forceRealHitTest = YES;
1870     NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
1871     forceRealHitTest = NO;
1872     
1873     if ([hitView isKindOfClass:[self class]]) {
1874         WebHTMLView *hitHTMLView = (WebHTMLView *)hitView;
1875         [[hitHTMLView _bridge] setActivationEventNumber:[event eventNumber]];
1876         return [self _isSelectionEvent:event] ? [[hitHTMLView _bridge] eventMayStartDrag:event] : NO;
1877     } else {
1878         return [hitView acceptsFirstMouse:event];
1879     }
1880 }
1881
1882 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
1883 {
1884     // We hack AK's hitTest method to catch all events at the topmost WebHTMLView.  However, for
1885     // the purposes of this method we want to really query the deepest view, so we forward to it.
1886     forceRealHitTest = YES;
1887     NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
1888     forceRealHitTest = NO;
1889     
1890     if ([hitView isKindOfClass:[self class]]) {
1891         WebHTMLView *hitHTMLView = (WebHTMLView *)hitView;
1892         return [self _isSelectionEvent:event] ? [[hitHTMLView _bridge] eventMayStartDrag:event] : NO;
1893     } else {
1894         return [hitView shouldDelayWindowOrderingForEvent:event];
1895     }
1896 }
1897
1898 - (void)mouseDown:(NSEvent *)event
1899 {    
1900     // TEXTINPUT: if there is marked text and the current input
1901     // manager wants to handle mouse events, we need to make sure to
1902     // pass it to them. If not, then we need to notify the input
1903     // manager when the marked text is abandoned (user clicks outside
1904     // the marked area)
1905
1906     [_private->compController endRevertingChange:NO moveLeft:NO];
1907
1908     // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
1909     // We don't want to pass them along to KHTML a second time.
1910     if ([event modifierFlags] & NSControlKeyMask) {
1911         return;
1912     }
1913     
1914     _private->ignoringMouseDraggedEvents = NO;
1915     
1916     // Record the mouse down position so we can determine drag hysteresis.
1917     [_private->mouseDownEvent release];
1918     _private->mouseDownEvent = [event retain];
1919
1920     // Don't do any mouseover while the mouse is down.
1921     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
1922
1923     // Let KHTML get a chance to deal with the event. This will call back to us
1924     // to start the autoscroll timer if appropriate.
1925     [[self _bridge] mouseDown:event];
1926 }
1927
1928 - (void)dragImage:(NSImage *)dragImage
1929                at:(NSPoint)at
1930            offset:(NSSize)offset
1931             event:(NSEvent *)event
1932        pasteboard:(NSPasteboard *)pasteboard
1933            source:(id)source
1934         slideBack:(BOOL)slideBack
1935 {   
1936     [self _stopAutoscrollTimer];
1937     
1938     _private->initiatedDrag = YES;
1939     [[self _webView] _setInitiatedDrag:YES];
1940     
1941     // Retain this view during the drag because it may be released before the drag ends.
1942     [self retain];
1943
1944     [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
1945 }
1946
1947 - (void)mouseDragged:(NSEvent *)event
1948 {
1949     // TEXTINPUT: if there is marked text and the current input
1950     // manager wants to handle mouse events, we need to make sure to
1951     // pass it to them.
1952
1953     if (!_private->ignoringMouseDraggedEvents) {
1954         [[self _bridge] mouseDragged:event];
1955     }
1956 }
1957
1958 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
1959 {
1960     if (_private->webCoreDragOp == NSDragOperationNone) {
1961         return (NSDragOperationGeneric | NSDragOperationCopy);
1962     } else {
1963         return _private->webCoreDragOp;
1964     }
1965 }
1966
1967 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
1968 {
1969     NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
1970     NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + _private->dragOffset.x, windowImageLoc.y + _private->dragOffset.y);
1971     [[self _bridge] dragSourceMovedTo:windowMouseLoc];
1972 }
1973
1974 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
1975 {
1976     NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
1977     NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + _private->dragOffset.x, windowImageLoc.y + _private->dragOffset.y);
1978     [[self _bridge] dragSourceEndedAt:windowMouseLoc operation:operation];
1979
1980     _private->initiatedDrag = NO;
1981     [[self _webView] _setInitiatedDrag:NO];
1982     
1983     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
1984     _private->ignoringMouseDraggedEvents = YES;
1985     
1986     // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
1987     // khtml expects to get balanced down/up's, so we must fake up a mouseup.
1988     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
1989                                             location:windowMouseLoc
1990                                        modifierFlags:[[NSApp currentEvent] modifierFlags]
1991                                            timestamp:[NSDate timeIntervalSinceReferenceDate]
1992                                         windowNumber:[[self window] windowNumber]
1993                                              context:[[NSApp currentEvent] context]
1994                                          eventNumber:0 clickCount:0 pressure:0];
1995     [self mouseUp:fakeEvent]; // This will also update the mouseover state.
1996     
1997     // Balance the previous retain from when the drag started.
1998     [self release];
1999 }
2000
2001 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
2002 {
2003     ASSERT(_private->draggingImageURL);
2004     
2005     NSFileWrapper *wrapper = [[self _dataSource] _fileWrapperForURL:_private->draggingImageURL];
2006     ASSERT(wrapper);    
2007     
2008     // FIXME: Report an error if we fail to create a file.
2009     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
2010     path = [[NSFileManager defaultManager] _web_pathWithUniqueFilenameForPath:path];
2011     if (![wrapper writeToFile:path atomically:NO updateFilenames:YES]) {
2012         ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
2013     }
2014     
2015     return [NSArray arrayWithObject:[path lastPathComponent]];
2016 }
2017
2018 - (BOOL)_canProcessDragWithDraggingInfo:(id <NSDraggingInfo>)draggingInfo
2019 {
2020     NSPasteboard *pasteboard = [draggingInfo draggingPasteboard];
2021     NSMutableSet *types = [NSMutableSet setWithArray:[pasteboard types]];
2022     [types intersectSet:[NSSet setWithArray:[WebHTMLView _insertablePasteboardTypes]]];
2023     if ([types count] == 0) {
2024         return NO;
2025     } else if ([types count] == 1 && 
2026                [types containsObject:NSFilenamesPboardType] && 
2027                ![self _imageExistsAtPaths:[pasteboard propertyListForType:NSFilenamesPboardType]]) {
2028         return NO;
2029     }
2030     
2031     NSPoint point = [self convertPoint:[draggingInfo draggingLocation] fromView:nil];
2032     NSDictionary *element = [self elementAtPoint:point];
2033     if ([[self _webView] isEditable] || [[element objectForKey:WebElementDOMNodeKey] isContentEditable]) {
2034         if (_private->initiatedDrag && [[element objectForKey:WebElementIsSelectedKey] boolValue]) {
2035             // Can't drag onto the selection being dragged.
2036             return NO;
2037         }
2038         return YES;
2039     }
2040     
2041     return NO;
2042 }
2043
2044 - (BOOL)_isMoveDrag
2045 {
2046     return _private->initiatedDrag && 
2047     [[self _bridge] isSelectionEditable] &&
2048     ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) == 0;
2049 }
2050
2051 - (NSDragOperation)draggingUpdatedWithDraggingInfo:(id <NSDraggingInfo>)draggingInfo actionMask:(unsigned int)actionMask
2052 {
2053     NSDragOperation operation = NSDragOperationNone;
2054     
2055     if (actionMask & WebDragDestinationActionDHTML) {
2056         operation = [[self _bridge] dragOperationForDraggingInfo:draggingInfo];
2057     }
2058     _private->webCoreHandlingDrag = (operation != NSDragOperationNone);
2059     
2060     if ((actionMask & WebDragDestinationActionEdit) &&
2061         !_private->webCoreHandlingDrag
2062         && [self _canProcessDragWithDraggingInfo:draggingInfo]) {
2063         WebView *webView = [self _webView];
2064         [webView moveDragCaretToPoint:[webView convertPoint:[draggingInfo draggingLocation] fromView:nil]];
2065         operation = [self _isMoveDrag] ? NSDragOperationMove : NSDragOperationCopy;
2066     } else {
2067         [[self _webView] removeDragCaret];
2068     }
2069     
2070     return operation;
2071 }
2072
2073 - (void)draggingCancelledWithDraggingInfo:(id <NSDraggingInfo>)draggingInfo
2074 {
2075     [[self _bridge] dragExitedWithDraggingInfo:draggingInfo];
2076     [[self _webView] removeDragCaret];
2077 }
2078
2079 - (BOOL)concludeDragForDraggingInfo:(id <NSDraggingInfo>)draggingInfo actionMask:(unsigned int)actionMask
2080 {
2081     WebView *webView = [self _webView];
2082     WebBridge *bridge = [self _bridge];
2083     if (_private->webCoreHandlingDrag) {
2084         ASSERT(actionMask & WebDragDestinationActionDHTML);
2085         [[webView _UIDelegateForwarder] webView:webView willPerformDragDestinationAction:WebDragDestinationActionDHTML forDraggingInfo:draggingInfo];
2086         [bridge concludeDragForDraggingInfo:draggingInfo];
2087         return YES;
2088     } else if (actionMask & WebDragDestinationActionEdit) {
2089         BOOL didInsert = NO;
2090         if ([self _canProcessDragWithDraggingInfo:draggingInfo]) {
2091             DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:[draggingInfo draggingPasteboard] allowPlainText:YES];
2092             if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[bridge dragCaretDOMRange] givenAction:WebViewInsertActionDropped]) {
2093                 [[webView _UIDelegateForwarder] webView:webView willPerformDragDestinationAction:WebDragDestinationActionEdit forDraggingInfo:draggingInfo];
2094                 if ([self _isMoveDrag]) {
2095                     [bridge moveSelectionToDragCaret:fragment];
2096                 } else {
2097                     [bridge setSelectionToDragCaret];
2098                     [bridge replaceSelectionWithFragment:fragment selectReplacement:YES];
2099                 }
2100                 didInsert = YES;
2101             }
2102         }
2103         [webView removeDragCaret];
2104         return didInsert;
2105     }
2106     return NO;
2107 }
2108
2109 - (NSDictionary *)elementAtPoint:(NSPoint)point
2110 {
2111     NSDictionary *elementInfoWC = [[self _bridge] elementAtPoint:point];
2112     NSMutableDictionary *elementInfo = [elementInfoWC mutableCopy];
2113     
2114     // Convert URL strings to NSURLs
2115     [elementInfo _web_setObjectIfNotNil:[NSURL _web_URLWithDataAsString:[elementInfoWC objectForKey:WebElementLinkURLKey]] forKey:WebElementLinkURLKey];
2116     [elementInfo _web_setObjectIfNotNil:[NSURL _web_URLWithDataAsString:[elementInfoWC objectForKey:WebElementImageURLKey]] forKey:WebElementImageURLKey];
2117     
2118     WebFrameView *webFrameView = [self _web_parentWebFrameView];
2119     ASSERT(webFrameView);
2120     WebFrame *webFrame = [webFrameView webFrame];
2121     
2122     if (webFrame) {
2123         NSString *frameName = [elementInfoWC objectForKey:WebElementLinkTargetFrameKey];
2124         if ([frameName length] == 0) {
2125             [elementInfo setObject:webFrame forKey:WebElementLinkTargetFrameKey];
2126         } else {
2127             WebFrame *wf = [webFrame findFrameNamed:frameName];
2128             if (wf != nil)
2129                 [elementInfo setObject:wf forKey:WebElementLinkTargetFrameKey];
2130             else
2131                 [elementInfo removeObjectForKey:WebElementLinkTargetFrameKey];
2132         }
2133         
2134         [elementInfo setObject:webFrame forKey:WebElementFrameKey];
2135     }
2136     
2137     return [elementInfo autorelease];
2138 }
2139
2140 - (void)mouseUp:(NSEvent *)event
2141 {
2142     // TEXTINPUT: if there is marked text and the current input
2143     // manager wants to handle mouse events, we need to make sure to
2144     // pass it to them.
2145
2146     [self _stopAutoscrollTimer];
2147     [[self _bridge] mouseUp:event];
2148     [self _updateMouseoverWithFakeEvent];
2149 }
2150
2151 - (void)mouseMovedNotification:(NSNotification *)notification
2152 {
2153     [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
2154 }
2155
2156 - (BOOL)supportsTextEncoding
2157 {
2158     return YES;
2159 }
2160
2161 - (NSView *)nextValidKeyView
2162 {
2163     NSView *view = nil;
2164     if (![self isHiddenOrHasHiddenAncestor]) {
2165         view = [[self _bridge] nextKeyViewInsideWebFrameViews];
2166     }
2167     if (view == nil) {
2168         view = [super nextValidKeyView];
2169     }
2170     return view;
2171 }
2172
2173 - (NSView *)previousValidKeyView
2174 {
2175     NSView *view = nil;
2176     if (![self isHiddenOrHasHiddenAncestor]) {
2177         view = [[self _bridge] previousKeyViewInsideWebFrameViews];
2178     }
2179     if (view == nil) {
2180         view = [super previousValidKeyView];
2181     }
2182     return view;
2183 }
2184
2185 - (BOOL)becomeFirstResponder
2186 {
2187     NSView *view = nil;
2188     if (![[self _webView] _isPerformingProgrammaticFocus]) {
2189         switch ([[self window] keyViewSelectionDirection]) {
2190         case NSDirectSelection:
2191             break;
2192         case NSSelectingNext:
2193             view = [[self _bridge] nextKeyViewInsideWebFrameViews];
2194             break;
2195         case NSSelectingPrevious:
2196             view = [[self _bridge] previousKeyViewInsideWebFrameViews];
2197             break;
2198         }
2199     }
2200     if (view) {
2201         [[self window] makeFirstResponder:view];
2202     }
2203     [self updateFocusDisplay];
2204     return YES;
2205 }
2206
2207 // This approach could be relaxed when dealing with 3228554.
2208 // Some alteration to the selection behavior was done to deal with 3672088.
2209 - (BOOL)resignFirstResponder
2210 {
2211     BOOL resign = [super resignFirstResponder];
2212     if (resign) {
2213         [_private->compController endRevertingChange:NO moveLeft:NO];
2214         _private->resigningFirstResponder = YES;
2215         if (![self maintainsInactiveSelection]) { 
2216             if ([[self _webView] _isPerformingProgrammaticFocus]) {
2217                 [self deselectText];
2218             }
2219             else {
2220                 [self deselectAll];
2221             }
2222         }
2223         [self updateFocusDisplay];
2224         _private->resigningFirstResponder = NO;
2225     }
2226     return resign;
2227 }
2228
2229 //------------------------------------------------------------------------------------
2230 // WebDocumentView protocol
2231 //------------------------------------------------------------------------------------
2232 - (void)setDataSource:(WebDataSource *)dataSource 
2233 {
2234 }
2235
2236 - (void)dataSourceUpdated:(WebDataSource *)dataSource
2237 {
2238 }
2239
2240 // Does setNeedsDisplay:NO as a side effect when printing is ending.
2241 // pageWidth != 0 implies we will relayout to a new width
2242 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
2243 {
2244     WebFrame *frame = [self _frame];
2245     NSArray *subframes = [frame childFrames];
2246     unsigned n = [subframes count];
2247     unsigned i;
2248     for (i = 0; i != n; ++i) {
2249         WebFrame *subframe = [subframes objectAtIndex:i];
2250         WebFrameView *frameView = [subframe frameView];
2251         if ([[subframe dataSource] _isDocumentHTML]) {
2252             [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:adjustViewSize];
2253         }
2254     }
2255
2256     if (printing != _private->printing) {
2257         [_private->pageRects release];
2258         _private->pageRects = nil;
2259         _private->printing = printing;
2260         [self setNeedsToApplyStyles:YES];
2261         [self setNeedsLayout:YES];
2262         [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
2263         if (printing) {
2264             [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
2265         } else {
2266             // Can't do this when starting printing or nested printing won't work, see 3491427.
2267             [self setNeedsDisplay:NO];
2268         }
2269     }
2270 }
2271
2272 // This is needed for the case where the webview is embedded in the view that's being printed.
2273 // It shouldn't be called when the webview is being printed directly.
2274 - (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit
2275 {
2276     // This helps when we print as part of a larger print process.
2277     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
2278     BOOL wasInPrintingMode = _private->printing;
2279     if (!wasInPrintingMode) {
2280         [self _setPrinting:YES minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
2281     }
2282     
2283     [[self _bridge] adjustPageHeightNew:newBottom top:oldTop bottom:oldBottom limit:bottomLimit];
2284     
2285     if (!wasInPrintingMode) {
2286         [self _setPrinting:NO minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:NO];
2287     }
2288 }
2289
2290 - (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
2291 {
2292     NSPrintInfo *printInfo = [printOperation printInfo];
2293     return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
2294 }
2295
2296 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
2297 {
2298     float viewWidth = NSWidth([self bounds]);
2299     if (viewWidth < 1) {
2300         ERROR("%@ has no width when printing", self);
2301         return 1.0;
2302     }
2303
2304     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
2305     float maxShrinkToFitScaleFactor = 1/PrintingMaximumShrinkFactor;
2306     float shrinkToFitScaleFactor = [self _availablePaperWidthForPrintOperation:printOperation]/viewWidth;
2307     return userScaleFactor * MAX(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor);
2308 }
2309
2310 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
2311 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
2312 // if AppKit makes it SPI/API.
2313 - (float)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation 
2314 {
2315     return [self _scaleFactorForPrintOperation:printOperation];
2316 }
2317
2318 - (void)setPageWidthForPrinting:(float)pageWidth
2319 {
2320     [self _setPrinting:NO minimumPageWidth:0. maximumPageWidth:0. adjustViewSize:NO];
2321     [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
2322 }
2323
2324
2325 // Return the number of pages available for printing
2326 - (BOOL)knowsPageRange:(NSRangePointer)range {
2327     // Must do this explicit display here, because otherwise the view might redisplay while the print
2328     // sheet was up, using printer fonts (and looking different).
2329     [self displayIfNeeded];
2330     [[self window] setAutodisplay:NO];
2331     
2332     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2333     // according to the paper size
2334     float minLayoutWidth = 0.0;
2335     float maxLayoutWidth = 0.0;
2336     if (![[self _bridge] isFrameSet]) {
2337         float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
2338         minLayoutWidth = paperWidth*PrintingMinimumShrinkFactor;
2339         maxLayoutWidth = paperWidth*PrintingMaximumShrinkFactor;
2340     }
2341     [self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; // will relayout
2342     
2343     // There is a theoretical chance that someone could do some drawing between here and endDocument,
2344     // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
2345     // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
2346
2347     range->location = 1;
2348     NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
2349     float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
2350     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
2351     [_private->pageRects release];
2352     NSArray *newPageRects = [[self _bridge] computePageRectsWithPrintWidthScaleFactor:userScaleFactor
2353                                                                           printHeight:[self _calculatePrintHeight]/totalScaleFactor];
2354     // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
2355     // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
2356     // a blank page (with correct-looking header and footer if that option is on), which matches
2357     // the behavior of IE and Camino at least.
2358     if ([newPageRects count] == 0) {
2359         newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect: NSMakeRect(0, 0, 1, 1)]];
2360     }
2361     _private->pageRects = [newPageRects retain];
2362     
2363     range->length = [_private->pageRects count];
2364     
2365     return YES;
2366 }
2367
2368 // Return the drawing rectangle for a particular page number
2369 - (NSRect)rectForPage:(int)page {
2370     return [[_private->pageRects objectAtIndex: (page-1)] rectValue];
2371 }
2372
2373 // Calculate the vertical size of the view that fits on a single page
2374 - (float)_calculatePrintHeight {
2375     // Obtain the print info object for the current operation
2376     NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
2377     
2378     // Calculate the page height in points
2379     NSSize paperSize = [pi paperSize];
2380     return paperSize.height - [pi topMargin] - [pi bottomMargin];
2381 }
2382
2383 - (void)drawPageBorderWithSize:(NSSize)borderSize
2384 {
2385     ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));    
2386     [[self _webView] _drawHeaderAndFooter];
2387 }
2388
2389 - (void)endDocument
2390 {
2391     [super endDocument];
2392     // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO 
2393     [self _setPrinting:NO minimumPageWidth:0.0 maximumPageWidth:0.0 adjustViewSize:YES];
2394     [[self window] setAutodisplay:YES];
2395 }
2396
2397 - (void)_updateTextSizeMultiplier
2398 {
2399     [[self _bridge] setTextSizeMultiplier:[[self _webView] textSizeMultiplier]];    
2400 }
2401
2402 - (BOOL)performKeyEquivalent:(NSEvent *)event
2403 {
2404     // Pass command-key combos through WebCore if there is a key binding available for
2405     // this event. This lets web pages have a crack at intercepting command-modified keypresses.
2406     if ([self _web_firstResponderIsSelfOrDescendantView] && [[self _bridge] interceptKeyEvent:event toView:self]) {
2407         return YES;
2408     }
2409     return [super performKeyEquivalent:event];
2410 }
2411
2412 - (BOOL)_interceptEditingKeyEvent:(NSEvent *)event
2413 {   
2414     // Work around this bug:
2415     // <rdar://problem/3630640>: "Calling interpretKeyEvents: in a custom text view can fail to process keys right after app startup"
2416     [NSKeyBindingManager sharedKeyBindingManager];
2417     
2418     // Use the isEditable state to determine whether or not to process tab key events.
2419     // The idea here is that isEditable will be NO when this WebView is being used
2420     // in a browser, and we desire the behavior where tab moves to the next element
2421     // in tab order. If isEditable is YES, it is likely that the WebView is being
2422     // embedded as the whole view, as in Mail, and tabs should input tabs as expected
2423     // in a text editor.
2424     if (![[self _webView] isEditable] && [event _web_isTabKeyEvent]) 
2425         return NO;
2426     
2427     // Now process the key normally
2428     [self interpretKeyEvents:[NSArray arrayWithObject:event]];
2429     return YES;
2430 }
2431
2432 - (void)keyDown:(NSEvent *)event
2433 {
2434     WebBridge *bridge = [self _bridge];
2435     if ([bridge interceptKeyEvent:event toView:self]) {
2436         // WebCore processed a key event, bail on any outstanding complete: UI
2437         [_private->compController endRevertingChange:YES moveLeft:NO];
2438         return;
2439     }
2440
2441     if (_private->compController && [_private->compController filterKeyDown:event])
2442         return;     // consumed by complete: popup window
2443
2444     // We're going to process a key event, bail on any outstanding complete: UI
2445     [_private->compController endRevertingChange:YES moveLeft:NO];
2446     
2447     if ([self _canType] && [self _interceptEditingKeyEvent:event]) 
2448         return;
2449     
2450     [super keyDown:event];
2451 }
2452
2453 - (void)keyUp:(NSEvent *)event
2454 {
2455     if (![[self _bridge] interceptKeyEvent:event toView:self]) {
2456         [super keyUp:event];
2457     }
2458 }
2459
2460 - (id)accessibilityAttributeValue:(NSString*)attributeName
2461 {
2462     if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
2463         id accTree = [[self _bridge] accessibilityTree];
2464         if (accTree)
2465             return [NSArray arrayWithObject: accTree];
2466         return nil;
2467     }
2468     return [super accessibilityAttributeValue:attributeName];
2469 }
2470
2471 - (id)accessibilityHitTest:(NSPoint)point
2472 {
2473     id accTree = [[self _bridge] accessibilityTree];
2474     if (accTree) {
2475         NSPoint windowCoord = [[self window] convertScreenToBase: point];
2476         return [accTree accessibilityHitTest: [self convertPoint:windowCoord fromView:nil]];
2477     }
2478     else
2479         return self;
2480 }
2481
2482 - (void)centerSelectionInVisibleArea:(id)sender
2483 {
2484     // FIXME: Does this do the right thing when the selection is not a caret?
2485     [[self _bridge] ensureCaretVisible];
2486 }
2487
2488 - (void)_alterCurrentSelection:(WebSelectionAlteration)alteration direction:(WebSelectionDirection)direction granularity:(WebSelectionGranularity)granularity
2489 {
2490     WebBridge *bridge = [self _bridge];
2491     DOMRange *proposedRange = [bridge rangeByAlteringCurrentSelection:alteration direction:direction granularity:granularity];
2492     WebView *webView = [self _webView];
2493     if ([[webView _editingDelegateForwarder] webView:webView shouldChangeSelectedDOMRange:[bridge selectedDOMRange] toDOMRange:proposedRange affinity:[bridge selectionAffinity] stillSelecting:NO]) {
2494         [bridge alterCurrentSelection:alteration direction:direction granularity:granularity];
2495     }
2496 }
2497
2498 - (void)moveBackward:(id)sender
2499 {
2500     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectBackward granularity:WebSelectByCharacter];
2501 }
2502
2503 - (void)moveBackwardAndModifySelection:(id)sender
2504 {
2505     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectBackward granularity:WebSelectByCharacter];
2506 }
2507
2508 - (void)moveDown:(id)sender
2509 {
2510     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectForward granularity:WebSelectByLine];
2511 }
2512
2513 - (void)moveDownAndModifySelection:(id)sender
2514 {
2515     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectForward granularity:WebSelectByLine];
2516 }
2517
2518 - (void)moveForward:(id)sender
2519 {
2520     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectForward granularity:WebSelectByCharacter];
2521 }
2522
2523 - (void)moveForwardAndModifySelection:(id)sender
2524 {
2525     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectForward granularity:WebSelectByCharacter];
2526 }
2527
2528 - (void)moveLeft:(id)sender
2529 {
2530     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectLeft granularity:WebSelectByCharacter];
2531 }
2532
2533 - (void)moveLeftAndModifySelection:(id)sender
2534 {
2535     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectLeft granularity:WebSelectByCharacter];
2536 }
2537
2538 - (void)moveRight:(id)sender
2539 {
2540     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectRight granularity:WebSelectByCharacter];
2541 }
2542
2543 - (void)moveRightAndModifySelection:(id)sender
2544 {
2545     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectRight granularity:WebSelectByCharacter];
2546 }
2547
2548 - (void)moveToBeginningOfDocument:(id)sender
2549 {
2550     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectLeft granularity:WebSelectByDocument];
2551 }
2552
2553 - (void)moveToBeginningOfDocumentAndModifySelection:(id)sender
2554 {
2555     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectLeft granularity:WebSelectByDocument];
2556 }
2557
2558 - (void)moveToBeginningOfLine:(id)sender
2559 {
2560     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectLeft granularity:WebSelectToLineBoundary];
2561 }
2562
2563 - (void)moveToBeginningOfLineAndModifySelection:(id)sender
2564 {
2565     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectLeft granularity:WebSelectToLineBoundary];
2566 }
2567
2568 - (void)moveToBeginningOfParagraph:(id)sender
2569 {
2570     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectLeft granularity:WebSelectByParagraph];
2571 }
2572
2573 - (void)moveToBeginningOfParagraphAndModifySelection:(id)sender
2574 {
2575     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectLeft granularity:WebSelectByParagraph];
2576 }
2577
2578 - (void)moveToEndOfDocument:(id)sender
2579 {
2580     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectRight granularity:WebSelectByDocument];
2581 }
2582
2583 - (void)moveToEndOfDocumentAndModifySelection:(id)sender
2584 {
2585     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectRight granularity:WebSelectByDocument];
2586 }
2587
2588 - (void)moveToEndOfLine:(id)sender
2589 {
2590     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectRight granularity:WebSelectToLineBoundary];
2591 }
2592
2593 - (void)moveToEndOfLineAndModifySelection:(id)sender
2594 {
2595     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectRight granularity:WebSelectToLineBoundary];
2596 }
2597
2598 - (void)moveToEndOfParagraph:(id)sender
2599 {
2600     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectRight granularity:WebSelectByParagraph];
2601 }
2602
2603 - (void)moveToEndOfParagraphAndModifySelection:(id)sender
2604 {
2605     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectRight granularity:WebSelectByParagraph];
2606 }
2607
2608 - (void)moveUp:(id)sender
2609 {
2610     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectBackward granularity:WebSelectByLine];
2611 }
2612
2613 - (void)moveUpAndModifySelection:(id)sender
2614 {
2615     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectBackward granularity:WebSelectByLine];
2616 }
2617
2618 - (void)moveWordBackward:(id)sender
2619 {
2620     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectBackward granularity:WebSelectByWord];
2621 }
2622
2623 - (void)moveWordBackwardAndModifySelection:(id)sender
2624 {
2625     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectBackward granularity:WebSelectByWord];
2626 }
2627
2628 - (void)moveWordForward:(id)sender
2629 {
2630     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectForward granularity:WebSelectByWord];
2631 }
2632
2633 - (void)moveWordForwardAndModifySelection:(id)sender
2634 {
2635     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectForward granularity:WebSelectByWord];
2636 }
2637
2638 - (void)moveWordLeft:(id)sender
2639 {
2640     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectLeft granularity:WebSelectByWord];
2641 }
2642
2643 - (void)moveWordLeftAndModifySelection:(id)sender
2644 {
2645     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectLeft granularity:WebSelectByWord];
2646 }
2647
2648 - (void)moveWordRight:(id)sender
2649 {
2650     [self _alterCurrentSelection:WebSelectByMoving direction:WebSelectRight granularity:WebSelectByWord];
2651 }
2652
2653 - (void)moveWordRightAndModifySelection:(id)sender
2654 {
2655     [self _alterCurrentSelection:WebSelectByExtending direction:WebSelectRight granularity:WebSelectByWord];
2656 }
2657
2658 - (void)pageDown:(id)sender
2659 {
2660     // FIXME: This should to scroll page down, then move the caret to the top left.
2661     ERROR("unimplemented");
2662 }
2663
2664 - (void)pageUp:(id)sender
2665 {
2666     // FIXME: This should to scroll page up, then move the caret to the top left.
2667     ERROR("unimplemented");
2668 }
2669
2670 - (void)_expandSelectionToGranularity:(WebSelectionGranularity)granularity
2671 {
2672     WebBridge *bridge = [self _bridge];
2673     DOMRange *range = [bridge rangeByExpandingSelectionWithGranularity:granularity];
2674     if (range && ![range collapsed]) {
2675         WebView *webView = [self _webView];
2676         if ([[webView _editingDelegateForwarder] webView:webView shouldChangeSelectedDOMRange:[bridge selectedDOMRange] toDOMRange:range affinity:[bridge selectionAffinity] stillSelecting:NO]) {
2677             [bridge setSelectedDOMRange:range affinity:[bridge selectionAffinity]];
2678         }
2679     }
2680 }
2681
2682 - (void)selectParagraph:(id)sender
2683 {
2684     [self _expandSelectionToGranularity:WebSelectByParagraph];
2685 }
2686
2687 - (void)selectLine:(id)sender
2688 {
2689     [self _expandSelectionToGranularity:WebSelectByLine];
2690 }
2691
2692 - (void)selectWord:(id)sender
2693 {
2694     [self _expandSelectionToGranularity:WebSelectByWord];
2695 }
2696
2697 - (void)copy:(id)sender
2698 {
2699     if ([[self _bridge] tryDHTMLCopy]) {
2700         return;     // DHTML did the whole operation
2701     }
2702
2703     if (![self _canCopy]) {
2704         NSBeep();
2705         return;
2706     }
2707     [self _writeSelectionToPasteboard:[NSPasteboard generalPasteboard]];
2708 }
2709
2710 - (void)cut:(id)sender
2711 {
2712     if ([[self _bridge] tryDHTMLCut]) {
2713         return;     // DHTML did the whole operation
2714     }
2715
2716     if (![self _canCut]) {
2717         NSBeep();
2718         return;
2719     }
2720     [self copy:sender];
2721     [[self _bridge] deleteSelection];
2722 }
2723
2724 - (void)delete:(id)sender
2725 {
2726     if (![self _canDelete]) {
2727         NSBeep();
2728         return;
2729     }
2730     [[self _bridge] deleteSelection];
2731 }
2732
2733 - (void)paste:(id)sender
2734 {
2735     if ([[self _bridge] tryDHTMLPaste]) {
2736         return;     // DHTML did the whole operation
2737     }
2738
2739     if (![self _canPaste]) {
2740         NSBeep();
2741         return;
2742     }
2743
2744     [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:YES];
2745 }
2746
2747 - (void)copyFont:(id)sender
2748 {
2749     // Font is RTF with a single character in it.
2750     // NSTextView uses the first character in the selection, or a space if there are no characters.
2751     ERROR("unimplemented");
2752 }
2753
2754 - (void)pasteFont:(id)sender
2755 {
2756     // FIXME: Support RTF to HTML (or DOM) conversion.
2757     // Font is RTF. Call fontAttributesInRange to extract the attributes to apply.
2758     // Then convert the RTF into CSS.
2759     ERROR("unimplemented");
2760 }
2761
2762 - (void)pasteAsPlainText:(id)sender
2763 {
2764     NSString *text = [[NSPasteboard generalPasteboard] stringForType:NSStringPboardType];
2765     WebBridge *bridge = [self _bridge];
2766     if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted]) {
2767         [bridge replaceSelectionWithText:text selectReplacement:NO];
2768     }
2769 }
2770
2771 - (void)pasteAsRichText:(id)sender
2772 {
2773     [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO];
2774 }
2775
2776 - (DOMCSSStyleDeclaration *)_fontManagerOperationAsStyle
2777 {
2778     WebBridge *bridge = [self _bridge];
2779     DOMCSSStyleDeclaration *style = [[bridge DOMDocument] createCSSStyleDeclaration];
2780
2781     NSFontManager *fm = [NSFontManager sharedFontManager];
2782
2783     NSFont *a = [fm convertFont:[fm fontWithFamily:@"Helvetica" traits:0 weight:5 size:10]];
2784     NSFont *b = [fm convertFont:[fm fontWithFamily:@"Times" traits:(NSBoldFontMask | NSItalicFontMask) weight:10 size:12]];
2785     
2786     NSString *fa = [a familyName];
2787     NSString *fb = [b familyName];
2788     if ([fa isEqualToString:fb]) {
2789         [style setFontFamily:fa];
2790     }
2791
2792     int sa = [a pointSize];
2793     int sb = [b pointSize];
2794     if (sa == sb) {
2795         [style setFontSize:[NSString stringWithFormat:@"%dpx", sa]];
2796     }
2797
2798     int wa = [fm weightOfFont:a];
2799     int wb = [fm weightOfFont:b];
2800     if (wa == wb) {
2801         if (wa >= 9) {
2802             [style setFontWeight:@"bold"];
2803         } else {
2804             [style setFontWeight:@"normal"];
2805         }
2806     }
2807
2808     BOOL ia = ([fm traitsOfFont:a] & NSItalicFontMask) != 0;
2809     BOOL ib = ([fm traitsOfFont:b] & NSItalicFontMask) != 0;
2810     if (ia == ib) {
2811         if (ia) {
2812             [style setFontStyle:@"italic"];
2813         } else {
2814             [style setFontStyle:@"normal"];
2815         }
2816     }
2817
2818     return style;
2819 }
2820
2821 - (void)changeFont:(id)sender
2822 {
2823     DOMCSSStyleDeclaration *style = [self _fontManagerOperationAsStyle];
2824     WebView *webView = [self _webView];
2825     WebBridge *bridge = [self _bridge];
2826     if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:[bridge selectedDOMRange]]) {
2827         [bridge applyStyle:style];
2828     }
2829 }
2830
2831 - (void)changeAttributes:(id)sender
2832 {
2833     // Used for various fancy stuff from the font panel.
2834     // Turn the attributes into CSS and then call applyStyle.
2835     ERROR("unimplemented");
2836 }
2837
2838 - (DOMCSSStyleDeclaration *)_colorPanelColorAsStyleUsingSelector:(SEL)selector
2839 {
2840     WebBridge *bridge = [self _bridge];
2841     DOMCSSStyleDeclaration *style = [[bridge DOMDocument] createCSSStyleDeclaration];
2842     NSColor *color = [[NSColorPanel sharedColorPanel] color];
2843     NSString *colorAsString = [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", [color redComponent]*255, [color greenComponent]*255, [color blueComponent]*255];
2844     ASSERT([style respondsToSelector:selector]);
2845     [style performSelector:selector withObject:colorAsString];
2846     
2847     return style;
2848 }
2849
2850 - (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range
2851 {
2852     DOMCSSStyleDeclaration *style = [self _colorPanelColorAsStyleUsingSelector:selector];
2853     WebView *webView = [self _webView];
2854     if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:range]) {
2855         [[self _bridge] applyStyle:style];
2856     }
2857 }
2858
2859 - (DOMRange *)_entireDOMRange
2860 {
2861     DOMDocument *document = [[self _bridge] DOMDocument];
2862     DOMRange *range = [document createRange];
2863     [range selectNode:[document documentElement]];
2864     return range;
2865 }
2866
2867 - (void)changeDocumentBackgroundColor:(id)sender
2868 {
2869     // Mimicking NSTextView, this method sets the background color for the
2870     // entire document. There is no NSTextView API for setting the background
2871     // color on the selected range only. Note that this method is currently
2872     // never called from the UI (see comment in changeColor:).
2873     // FIXME: this actually has no effect when called, probably due to 3654850. _entireDOMRange seems
2874     // to do the right thing because it works in startSpeaking:, and I know setBackgroundColor: does the
2875     // right thing because I tested it with [[self _bridge] selectedDOMRange].
2876     // FIXME: This won't actually apply the style to the entire range here, because it ends up calling
2877     // [bridge applyStyle:], which operates on the current selection. To make this work right, we'll
2878     // need to save off the selection, temporarily set it to the entire range, make the change, then
2879     // restore the old selection.
2880     [self _changeCSSColorUsingSelector:@selector(setBackgroundColor:) inRange:[self _entireDOMRange]];
2881 }
2882
2883 - (void)changeColor:(id)sender
2884 {
2885     // FIXME: in NSTextView, this method calls changeDocumentBackgroundColor: when a
2886     // private call has earlier been made by [NSFontFontEffectsBox changeColor:], see 3674493. 
2887     // AppKit will have to be revised to allow this to work with anything that isn't an 
2888     // NSTextView. However, this might not be required for Tiger, since the background-color 
2889     // changing box in the font panel doesn't work in Mail (3674481), though it does in TextEdit.
2890     [self _changeCSSColorUsingSelector:@selector(setColor:) inRange:[[self _bridge] selectedDOMRange]];
2891 }
2892
2893 - (void)_alignSelectionUsingCSSValue:(NSString *)CSSAlignmentValue
2894 {
2895     // FIXME 3675191: This doesn't work yet. Maybe it's blocked by 3654850, or maybe something other than
2896     // just applyStyle: needs to be called for block-level attributes like this.
2897     WebBridge *bridge = [self _bridge];
2898     DOMCSSStyleDeclaration *style = [[bridge DOMDocument] createCSSStyleDeclaration];
2899     [style setTextAlign:CSSAlignmentValue];
2900     WebView *webView = [self _webView];
2901     if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:[bridge selectedDOMRange]]) {
2902         [[self _bridge] applyStyle:style];
2903     }
2904 }
2905
2906 - (void)alignCenter:(id)sender
2907 {
2908     [self _alignSelectionUsingCSSValue:@"center"];
2909 }
2910
2911 - (void)alignJustified:(id)sender
2912 {
2913     [self _alignSelectionUsingCSSValue:@"justify"];
2914 }
2915
2916 - (void)alignLeft:(id)sender
2917 {
2918     [self _alignSelectionUsingCSSValue:@"left"];
2919 }
2920
2921 - (void)alignRight:(id)sender
2922 {
2923     [self _alignSelectionUsingCSSValue:@"right"];
2924 }
2925
2926 - (void)indent:(id)sender
2927 {
2928     // Figure out current indent level.
2929     // Turn new indent level into CSS, then call applyStyle:.
2930     ERROR("unimplemented");
2931 }
2932
2933 - (void)insertTab:(id)sender
2934 {
2935     WebBridge *bridge = [self _bridge];
2936     if ([self _shouldReplaceSelectionWithText:@"\t" givenAction:WebViewInsertActionPasted]) {
2937         [bridge insertText:@"\t"];
2938     }
2939 }
2940
2941 - (void)insertBacktab:(id)sender
2942 {
2943     // Doing nothing matches normal NSTextView behavior. If we ever use WebView for a field-editor-type purpose
2944     // we might add code here.
2945 }
2946
2947 - (void)insertNewline:(id)sender
2948 {
2949     // Perhaps we should make this delegate call sensitive to the real DOM operation we actually do.
2950     WebBridge *bridge = [self _bridge];
2951     if ([self _shouldReplaceSelectionWithText:@"\n" givenAction:WebViewInsertActionTyped]) {
2952         [bridge insertNewline];
2953     }
2954 }
2955
2956 - (void)insertParagraphSeparator:(id)sender
2957 {
2958     // FIXME: Should this do something different?
2959     [self insertNewline:sender];
2960 }
2961
2962 - (void)changeCaseOfLetter:(id)sender
2963 {
2964     ERROR("unimplemented");
2965 }
2966
2967 - (void)_changeWordCaseWithSelector:(SEL)selector
2968 {
2969     WebBridge *bridge = [self _bridge];
2970     [self selectWord:nil];
2971     NSString *word = [[bridge selectedString] performSelector:selector];
2972     // FIXME: Does this need a different action context other than "typed"?
2973     if ([self _shouldReplaceSelectionWithText:word givenAction:WebViewInsertActionTyped]) {
2974         [bridge replaceSelectionWithText:word selectReplacement:NO];
2975     }
2976 }
2977
2978 - (void)uppercaseWord:(id)sender
2979 {
2980     [self _changeWordCaseWithSelector:@selector(uppercaseString)];
2981 }
2982
2983 - (void)lowercaseWord:(id)sender
2984 {
2985     [self _changeWordCaseWithSelector:@selector(lowercaseString)];
2986 }
2987
2988 - (void)capitalizeWord:(id)sender
2989 {
2990     [self _changeWordCaseWithSelector:@selector(capitalizedString)];
2991 }
2992
2993 - (void)deleteForward:(id)sender
2994 {
2995     ERROR("unimplemented");
2996 }
2997
2998 - (void)deleteBackward:(id)sender
2999 {
3000     WebBridge *bridge = [self _bridge];
3001     if ([bridge isSelectionEditable]) {
3002         WebView *webView = [self _webView];
3003         if ([[webView _editingDelegateForwarder] webView:webView shouldDeleteDOMRange:[bridge selectedDOMRange]]) {
3004             [bridge deleteKeyPressed];
3005         }
3006     }
3007 }
3008
3009 - (void)deleteBackwardByDecomposingPreviousCharacter:(id)sender
3010 {
3011     ERROR("unimplemented");
3012 }
3013
3014 - (void)deleteWordForward:(id)sender
3015 {
3016     [self moveWordForwardAndModifySelection:sender];
3017     [self delete:sender];
3018 }
3019
3020 - (void)deleteWordBackward:(id)sender
3021 {
3022     [self moveWordBackwardAndModifySelection:sender];
3023     [self delete:sender];
3024 }
3025
3026 - (void)deleteToBeginningOfLine:(id)sender
3027 {
3028     [self moveToBeginningOfLine:sender];
3029     [self delete:sender];
3030 }
3031
3032 - (void)deleteToEndOfLine:(id)sender
3033 {
3034     [self moveToEndOfLine:sender];
3035     [self delete:sender];
3036 }
3037
3038 - (void)deleteToBeginningOfParagraph:(id)sender
3039 {
3040     [self moveToBeginningOfParagraph:sender];
3041     [self delete:sender];
3042 }
3043
3044 - (void)deleteToEndOfParagraph:(id)sender
3045 {
3046     [self moveToEndOfParagraph:sender];
3047     [self delete:sender];
3048 }
3049
3050 - (void)complete:(id)sender
3051 {
3052     if (!_private->compController) {
3053         _private->compController = [[WebTextCompleteController alloc] initWithHTMLView:self];
3054     }
3055     [_private->compController doCompletion];
3056 }
3057
3058 - (void)checkSpelling:(id)sender
3059 {
3060     // WebCore does everything but update the spelling panel
3061     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3062     if (!checker) {
3063         ERROR("No NSSpellChecker");
3064         return;
3065     }
3066     NSString *badWord = [[self _bridge] advanceToNextMisspelling];
3067     if (badWord) {
3068         [checker updateSpellingPanelWithMisspelledWord:badWord];
3069     }
3070 }
3071 #if APPKIT_CODE_FOR_REFERENCE
3072     NSTextStorage *text = _getTextStorage(self);
3073     NSTextViewSharedData *sharedData = _getSharedData(self);
3074     if (text && ([text length] > 0) && [self isSelectable]) {
3075         NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3076         NSRange selCharRange = [self selectedRange];
3077         NSRange misspellCharRange = {0, 0}, grammarCharRange = {0, 0};
3078         unsigned i, count;
3079         NSArray *grammarRanges = nil, *grammarDescriptions = nil;
3080
3081         if (!checker || [checker windowIsSpellingPanel:[self window]]) return;
3082
3083         misspellCharRange = [checker checkSpellingOfString:[text string] startingAt:NSMaxRange(selCharRange) language:nil wrap:YES inSpellDocumentWithTag:[self spellCheckerDocumentTag] wordCount:NULL];
3084 #if GRAMMAR_CHECKING
3085         grammarCharRange = [checker checkGrammarOfString:[text string] startingAt:NSMaxRange(selCharRange) language:nil wrap:YES inSpellDocumentWithTag:[self spellCheckerDocumentTag] ranges:&grammarRanges descriptions:&grammarDescriptions reconnectOnError:YES];
3086 #endif
3087
3088         if (misspellCharRange.length > 0 && (grammarCharRange.length == 0 || misspellCharRange.location <= grammarCharRange.location)) {
3089             // select the text and drive the panel
3090             [self setSelectedRange:misspellCharRange affinity:NSSelectionAffinityUpstream stillSelecting:NO];
3091             if ([self isEditable]) {
3092                 [self _addSpellingAttributeForRange:misspellCharRange];
3093                 sharedData->_excludedSpellingCharRange = misspellCharRange;
3094             }
3095             [self scrollRangeToVisible:misspellCharRange];
3096             [checker updateSpellingPanelWithMisspelledWord:[[text string] substringWithRange:misspellCharRange]];
3097         } else if (grammarCharRange.length > 0) {
3098             [self setSelectedRange:grammarCharRange affinity:NSSelectionAffinityUpstream stillSelecting:NO];
3099             count = [grammarRanges count];
3100             if ([self isEditable]) {
3101                 for (i = 0; i < count; i++) {
3102                     NSRange range = [grammarRanges rangeAtIndex:i];
3103                     range.location += grammarCharRange.location;
3104                     [self _addSpellingAttributeForRange:range];
3105                     [_getLayoutManager(self) addTemporaryAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[grammarDescriptions objectAtIndex:i], NSToolTipAttributeName, nil] forCharacterRange:range];
3106                 }
3107             }
3108             [self scrollRangeToVisible:grammarCharRange];
3109         } else {
3110             // Cause the beep to indicate there are no more misspellings.
3111             [checker updateSpellingPanelWithMisspelledWord:@""];
3112         }
3113     }
3114 #endif
3115
3116
3117 - (void)showGuessPanel:(id)sender
3118 {
3119     // WebCore does everything but update the spelling panel
3120     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3121     if (!checker) {
3122         ERROR("No NSSpellChecker");
3123         return;
3124     }
3125
3126     NSString *badWord = [[self _bridge] advanceToNextMisspelling];
3127     if (badWord) {
3128         [checker updateSpellingPanelWithMisspelledWord:badWord];
3129     }
3130     [[checker spellingPanel] orderFront:sender];
3131 }
3132 #if APPKIT_CODE_FOR_REFERENCE
3133     NSTextStorage *text = _getTextStorage(self);
3134     NSTextViewSharedData *sharedData = _getSharedData(self);
3135     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3136     if (text && ([text length] > 0) && [self isSelectable]) {
3137         NSRange selCharRange = [self selectedRange];
3138         NSRange misspellCharRange = {0, 0};
3139         if (checker && ![checker windowIsSpellingPanel:[self window]]) {
3140             misspellCharRange = [checker checkSpellingOfString:[text string] startingAt:((selCharRange.location > 0) ? (selCharRange.location - 1) : 0) language:nil wrap:YES inSpellDocumentWithTag:[self spellCheckerDocumentTag] wordCount:NULL];
3141             if (misspellCharRange.length) {
3142                 // select the text and drive the panel
3143                 [self setSelectedRange:misspellCharRange affinity:NSSelectionAffinityUpstream stillSelecting:NO];
3144                 if ([self isEditable]) {
3145                     [self _addSpellingAttributeForRange:misspellCharRange];
3146                     sharedData->_excludedSpellingCharRange = misspellCharRange;
3147                 }
3148                 [self scrollRangeToVisible:misspellCharRange];
3149                 [checker updateSpellingPanelWithMisspelledWord:[[text string] substringWithRange:misspellCharRange]];
3150             }
3151         }
3152     }
3153     if (checker) {
3154         [[checker spellingPanel] orderFront:sender];
3155     }
3156 #endif
3157
3158 - (void)_changeSpellingToWord:(NSString *)newWord
3159 {
3160     WebBridge *bridge = [self _bridge];
3161     if (![bridge isSelectionEditable]) {
3162         return;
3163     }
3164     
3165     // Don't correct to empty string.  (AppKit checked this, we might as well too.)
3166     if (![NSSpellChecker sharedSpellChecker]) {
3167         ERROR("No NSSpellChecker");
3168         return;
3169     }
3170     
3171     if ([newWord isEqualToString:@""]) {
3172         return;
3173     }
3174
3175     if ([self _shouldReplaceSelectionWithText:newWord givenAction:WebViewInsertActionPasted]) {
3176         [bridge replaceSelectionWithText:newWord selectReplacement:YES];
3177     }
3178 }
3179 #if APPKIT_CODE_FOR_REFERENCE
3180     NSRange charRange = [self rangeForUserTextChange];
3181     if ([self isEditable] && charRange.location != NSNotFound) {
3182         NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3183         if (!checker || [checker windowIsSpellingPanel:[self window]]) return;
3184         
3185         // Don't correct to empty string.
3186         if ([newWord isEqualToString:@""]) return;
3187         
3188         if ([self shouldChangeTextInRange:charRange replacementString:newWord]) {
3189             [self replaceCharactersInRange:charRange withString:newWord];
3190             charRange.length = [newWord length];
3191             [self setSelectedRange:charRange affinity:NSSelectionAffinityUpstream stillSelecting:NO];
3192             [self didChangeText];
3193         }
3194     }
3195 #endif
3196
3197 - (void)changeSpelling:(id)sender {
3198     [self _changeSpellingToWord:[[sender selectedCell] stringValue]];
3199 }
3200
3201 - (void)ignoreSpelling:(id)sender {
3202     WebBridge *bridge = [self _bridge];
3203     if (![bridge isSelectionEditable]) {
3204         return;
3205     }
3206     
3207     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3208     if (!checker) {
3209         ERROR("No NSSpellChecker");
3210         return;
3211     }
3212     
3213     NSString *stringToIgnore = [sender stringValue];
3214     unsigned int length = [stringToIgnore length];
3215     if (stringToIgnore && length > 0) {
3216         [checker ignoreWord:stringToIgnore inSpellDocumentWithTag:[[self _webView] spellCheckerDocumentTag]];
3217         //??? need to clear any special markup for misspelled words
3218     }
3219 }
3220 #if APPKIT_CODE_FOR_REFERENCE
3221     NSRange charRange = [self rangeForUserTextChange];
3222     if ([self isEditable]) {
3223         NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3224         NSString *stringToIgnore;
3225         unsigned int length;
3226         
3227         if (!checker) return;
3228         
3229         stringToIgnore = [sender stringValue];
3230         length = [stringToIgnore length];
3231         if (stringToIgnore && length > 0) {
3232             [checker ignoreWord:stringToIgnore inSpellDocumentWithTag:[self spellCheckerDocumentTag]];
3233             if (length == charRange.length && [stringToIgnore isEqualToString:[[_getTextStorage(self) string] substringWithRange:charRange]]) {
3234                 [self _removeSpellingAttributeForRange:charRange];
3235             }
3236         }
3237     }
3238 #endif
3239
3240
3241 #if APPKIT_CODE_FOR_REFERENCE
3242
3243 - (void)_changeSpellingFromMenu:(id)sender {
3244     [self _changeSpellingToWord:[sender title]];
3245 }
3246
3247 - (void)_ignoreSpellingFromMenu:(id)sender {
3248     NSRange charRange = [self rangeForUserTextChange];
3249     if ([self isEditable] && charRange.location != NSNotFound && charRange.length > 0) {
3250         NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3251         if (!checker || [checker windowIsSpellingPanel:[self window]]) return;
3252         [self _removeSpellingAttributeForRange:charRange];
3253         [checker ignoreWord:[[_getTextStorage(self) string] substringWithRange:charRange] inSpellDocumentWithTag:[self spellCheckerDocumentTag]];
3254     }
3255 }
3256
3257 - (void)_learnSpellingFromMenu:(id)sender {
3258     NSRange charRange = [self rangeForUserTextChange];
3259     if ([self isEditable] && charRange.location != NSNotFound && charRange.length > 0) {
3260         NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3261         if (!checker || [checker windowIsSpellingPanel:[self window]]) return;
3262         [self _removeSpellingAttributeForRange:charRange];
3263         [checker learnWord:[[_getTextStorage(self) string] substringWithRange:charRange]];
3264     }
3265 }
3266
3267 - (void)_forgetSpellingFromMenu:(id)sender {
3268     NSRange charRange = [self rangeForUserTextChange];
3269     if ([self isEditable] && charRange.location != NSNotFound && charRange.length > 0) {
3270         NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3271         if (!checker || [checker windowIsSpellingPanel:[self window]]) return;
3272         [checker forgetWord:[[_getTextStorage(self) string] substringWithRange:charRange]];
3273     }
3274 }
3275
3276 - (void)_openLinkFromMenu:(id)sender {
3277     NSTextStorage *text = _getTextStorage(self);
3278     NSRange charRange = [self selectedRange];
3279     if (charRange.location != NSNotFound && charRange.length > 0) {
3280         id link = [text attribute:NSLinkAttributeName atIndex:charRange.location effectiveRange:NULL];
3281         if (link) {
3282             [self clickedOnLink:link atIndex:charRange.location];
3283         } else {
3284             NSString *string = [[text string] substringWithRange:charRange];
3285             link = [NSURL URLWithString:string];
3286             if (link) [[NSWorkspace sharedWorkspace] openURL:link];
3287         }
3288     }
3289 }
3290
3291 #endif
3292
3293 - (void)performFindPanelAction:(id)sender
3294 {
3295     ERROR("unimplemented");
3296 }
3297
3298 - (void)startSpeaking:(id)sender
3299 {
3300     WebBridge *bridge = [self _bridge];
3301     DOMRange *range = [bridge selectedDOMRange];
3302     if (!range || [range collapsed]) {
3303         range = [self _entireDOMRange];
3304     }
3305     [NSApp speakString:[bridge stringForRange:range]];
3306 }
3307
3308 - (void)stopSpeaking:(id)sender
3309 {
3310     [NSApp stopSpeaking:sender];
3311 }
3312
3313 #if 0
3314
3315 - (void)transpose:(id)sender;
3316 - (void)transposeWords:(id)sender;
3317 - (void)insertNewlineIgnoringFieldEditor:(id)sender;
3318 - (void)insertTabIgnoringFieldEditor:(id)sender;
3319 - (void)yank:(id)sender;
3320 - (void)yankAndSelect:(id)sender;
3321 - (void)setMark:(id)sender;
3322 - (void)deleteToMark:(id)sender;
3323 - (void)selectToMark:(id)sender;
3324 - (void)swapWithMark:(id)sender;
3325 - (void)moveParagraphBackwardAndModifySelection:(id)sender;
3326 - (void)moveParagraphForwardAndModifySelection:(id)sender;
3327 - (void)pageUpAndModifySelection:(id)sender;
3328 - (void)pageDownAndModifySelection:(id)sender;
3329 - (void)insertTable:(id)sender;
3330
3331 #endif
3332
3333 @end
3334
3335 @implementation WebHTMLView (WebTextSizing)
3336
3337 - (void)_web_textSizeMultiplierChanged
3338 {
3339     [self _updateTextSizeMultiplier];
3340 }
3341
3342 @end
3343
3344 @implementation NSArray (WebHTMLView)
3345
3346 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object
3347 {
3348     NSEnumerator *enumerator = [self objectEnumerator];
3349     WebNetscapePluginEmbeddedView *view;
3350     while ((view = [enumerator nextObject]) != nil) {
3351         if ([view isKindOfClass:[WebNetscapePluginEmbeddedView class]]) {
3352             [view performSelector:selector withObject:object];
3353         }
3354     }
3355 }
3356
3357 @end
3358
3359 @implementation WebElementOrTextFilter
3360
3361 + (WebElementOrTextFilter *)filter 
3362 {
3363     if (!elementOrTextFilterInstance)
3364         elementOrTextFilterInstance = [[WebElementOrTextFilter alloc] init];
3365     return elementOrTextFilterInstance;
3366 }
3367
3368 - (short)acceptNode:(DOMNode *)n
3369 {
3370     return ([n isKindOfClass:[DOMElement class]] || [n isKindOfClass:[DOMText class]]) ? DOM_FILTER_ACCEPT : DOM_FILTER_SKIP;
3371 }
3372
3373 @end
3374
3375 @implementation WebHTMLView (WebInternal)
3376
3377 - (void)_selectionChanged
3378 {
3379     [self _updateSelectionForInputManager];
3380     [self _updateFontPanel];
3381 }
3382
3383 - (void)_updateFontPanel
3384 {
3385     // FIXME: NSTextView bails out if becoming or resigning first responder, for which it has ivar flags. Not
3386     // sure if we need to do something similar.
3387     
3388     WebBridge *bridge = [self _bridge];
3389
3390     if (![bridge isSelectionEditable]) {
3391         return;
3392     }
3393     
3394     NSWindow *window = [self window];
3395     // FIXME: is this first-responder check correct? What happens if a subframe is editable and is first responder?
3396     if ([NSApp keyWindow] != window || [window firstResponder] != self) {
3397         return;
3398     }
3399     
3400     BOOL onlyOneFontInSelection = YES;
3401     NSFont *font = nil;
3402     
3403     if (![self _hasSelection]) {
3404         font = [bridge fontForCurrentPosition];
3405     } 
3406     else {
3407         DOMRange *selection = [bridge selectedDOMRange];
3408         DOMNode *startContainer = [selection startContainer];
3409         DOMNode *endContainer = [selection endContainer];
3410         
3411         ASSERT(startContainer);
3412         ASSERT(endContainer);
3413         ASSERT([[WebElementOrTextFilter filter] acceptNode:startContainer] == DOM_FILTER_ACCEPT);
3414         ASSERT([[WebElementOrTextFilter filter] acceptNode:endContainer] == DOM_FILTER_ACCEPT);
3415         
3416         font = [bridge renderedFontForNode:startContainer];
3417         
3418         if (startContainer != endContainer) {
3419             DOMDocument *document = [bridge DOMDocument];
3420             DOMTreeWalker *treeWalker = [document createTreeWalker:document :DOM_SHOW_ALL :[WebElementOrTextFilter filter] :NO];
3421             DOMNode *node = startContainer;
3422             [treeWalker setCurrentNode:node];
3423             while (node) {
3424                 NSFont *otherFont = [bridge renderedFontForNode:node];
3425                 if (![font isEqual:otherFont]) {
3426                     onlyOneFontInSelection = NO;
3427                     break;
3428                 }
3429                 if (node == endContainer)
3430                     break;
3431                 node = [treeWalker nextNode];
3432             }
3433         }
3434     }
3435     
3436     // FIXME: for now, return a bogus font that distinguishes the empty selection from the non-empty
3437     // selection. We should be able to remove this once the rest of this code works properly.
3438     if (font == nil) {
3439         if (![self _hasSelection]) {
3440             font = [NSFont toolTipsFontOfSize:17];
3441         } else {
3442             font = [NSFont menuFontOfSize:23];
3443         }
3444     }
3445     ASSERT(font != nil);
3446         
3447     NSFontManager *fm = [NSFontManager sharedFontManager];
3448     [fm setSelectedFont:font isMultiple:!onlyOneFontInSelection];
3449     // FIXME: we don't keep track of selected attributes, or set them on the font panel. This
3450     // appears to have no effect on the UI. E.g., underlined text in Mail or TextEdit is
3451     // not reflected in the font panel. Maybe someday this will change.
3452 }
3453
3454 - (unsigned int)_delegateDragSourceActionMask
3455 {
3456     WebView *webView = [self _webView];
3457     NSPoint point = [webView convertPoint:[_private->mouseDownEvent locationInWindow] fromView:nil];
3458     _private->dragSourceActionMask = [[webView _UIDelegateForwarder] webView:webView dragSourceActionMaskForPoint:point];
3459     return _private->dragSourceActionMask;
3460 }
3461
3462 @end
3463
3464
3465 @implementation WebHTMLView (WebNSTextInputSupport)
3466
3467 - (NSArray *)validAttributesForMarkedText
3468 {
3469     // FIXME: TEXTINPUT: validAttributesForMarkedText not yet implemented
3470     return [NSArray array];
3471 }
3472
3473 - (unsigned int)characterIndexForPoint:(NSPoint)thePoint
3474 {
3475     ERROR("TEXTINPUT: characterIndexForPoint: not yet implemented");
3476     return 0;
3477 }
3478
3479 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
3480 {
3481     ERROR("TEXTINPUT: firstRectForCharacterRange: not yet implemented");
3482     return NSMakeRect(0,0,0,0);
3483 }
3484
3485 - (NSRange)selectedRange
3486 {
3487     ERROR("TEXTINPUT: selectedRange not yet implemented");
3488     return NSMakeRange(0,0);
3489 }
3490
3491 - (NSRange)markedRange
3492 {
3493     if (![self hasMarkedText]) {
3494         return NSMakeRange(NSNotFound,0);
3495     }
3496
3497     DOMRange *markedDOMRange = [[self _bridge] markedDOMRange];
3498
3499     unsigned rangeLocation = [markedDOMRange startOffset];
3500     unsigned rangeLength = [markedDOMRange endOffset] - rangeLocation;
3501
3502     return NSMakeRange(rangeLocation, rangeLength);
3503 }
3504
3505 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange
3506 {
3507     ERROR("TEXTINPUT: attributedSubstringFromRange: not yet implemented");
3508     return nil;
3509 }
3510