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