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