Reviewed by me
[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/WebBridge.h>
9 #import <WebKit/WebClipView.h>
10 #import <WebKit/WebDataSourcePrivate.h>
11 #import <WebKit/WebDocumentInternal.h>
12 #import <WebKit/WebDOMDocument.h>
13 #import <WebKit/WebException.h>
14 #import <WebKit/WebFrame.h>
15 #import <WebKit/WebFramePrivate.h>
16 #import <WebKit/WebFrameViewPrivate.h>
17 #import <WebKit/WebHTMLViewPrivate.h>
18 #import <WebKit/WebNetscapePluginEmbeddedView.h>
19 #import <WebKit/WebKitLogging.h>
20 #import <WebKit/WebNSPasteboardExtras.h>
21 #import <WebKit/WebNSViewExtras.h>
22 #import <WebKit/WebPluginController.h>
23 #import <WebKit/WebTextRenderer.h>
24 #import <WebKit/WebTextRendererFactory.h>
25 #import <WebKit/WebUnicode.h>
26 #import <WebKit/WebViewPrivate.h>
27
28 #import <AppKit/NSGraphicsContextPrivate.h>
29 #import <AppKit/NSResponder_Private.h>
30 #import <CoreGraphics/CGContextGState.h>
31
32 #import <AppKit/NSAccessibility.h>
33
34 #import <WebKit/WebImageRenderer.h>
35 #import <WebKit/WebKitNSStringExtras.h>
36 #import <WebKit/WebNSEventExtras.h>
37 #import <WebKit/WebNSImageExtras.h>
38 #import <WebKit/WebNSURLExtras.h>
39 #import <WebKit/WebPreferences.h>
40 #import <WebKit/WebStringTruncator.h>
41
42 #import <Foundation/NSFileManager_NSURLExtras.h>
43 #import <Foundation/NSURL_NSURLExtras.h>
44
45 // The link drag hysteresis is much larger than the others because there
46 // needs to be enough space to cancel the link press without starting a link drag,
47 // and because dragging links is rare.
48 #define LinkDragHysteresis              40.0
49 #define ImageDragHysteresis             5.0
50 #define TextDragHysteresis              3.0
51 #define TextDragDelay                   0.15
52
53 // By imaging to a width a little wider than the available pixels,
54 // thin pages will be scaled down a little, matching the way they
55 // print in IE and Camino. This lets them use fewer sheets than they
56 // would otherwise, which is presumably why other browsers do this.
57 // Wide pages will be scaled down as necessary to fit their content, 
58 // so this factor only affects thin pages.
59 #define PrintingExtraWidthFactor        1.25
60
61 #define AUTOSCROLL_INTERVAL             0.1
62
63 #define DRAG_LABEL_BORDER_X             4.0
64 #define DRAG_LABEL_BORDER_Y             2.0
65 #define DRAG_LABEL_RADIUS               5.0
66 #define DRAG_LABEL_BORDER_Y_OFFSET              2.0
67
68 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP        120.0
69 #define MAX_DRAG_LABEL_WIDTH                    320.0
70
71 #define DRAG_LINK_LABEL_FONT_SIZE   11.0
72 #define DRAG_LINK_URL_FONT_SIZE   10.0
73
74 static BOOL forceRealHitTest = NO;
75
76 @interface WebHTMLView (WebHTMLViewPrivate)
77 - (void)_setPrinting:(BOOL)printing pageWidth:(float)pageWidth adjustViewSize:(BOOL)adjustViewSize;
78 - (void)_updateTextSizeMultiplier;
79 @end
80
81 // Any non-zero value will do, but using somethign recognizable might help us debug some day.
82 #define TRACKING_RECT_TAG 0xBADFACE
83
84
85 @interface NSView (AppKitSecretsIKnowAbout)
86 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
87 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
88 - (NSRect)_dirtyRect;
89 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
90 @end
91
92 @interface NSView (WebHTMLViewPrivate)
93 - (void)_web_setPrintingModeRecursive;
94 - (void)_web_clearPrintingModeRecursive;
95 - (void)_web_layoutIfNeededRecursive:(NSRect)rect testDirtyRect:(bool)testDirtyRect;
96 @end
97
98 @interface NSMutableDictionary (WebHTMLViewPrivate)
99 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
100 @end
101
102 @implementation WebHTMLViewPrivate
103
104 - (void)dealloc
105 {
106     ASSERT(autoscrollTimer == nil);
107     ASSERT(autoscrollTriggerEvent == nil);
108     
109     [pluginController destroyAllPlugins];
110     
111     [mouseDownEvent release];
112     [draggingImageURL release];
113     [pluginController release];
114     [toolTip release];
115     
116     [super dealloc];
117 }
118
119 @end
120
121
122 @implementation WebHTMLView (WebPrivate)
123
124 - (void)_reset
125 {
126     [WebImageRenderer stopAnimationsInView:self];
127 }
128
129 - (WebView *)_webView
130 {
131     // We used to use the view hierarchy exclusively here, but that won't work
132     // right when the first viewDidMoveToSuperview call is done, and this wil.
133     return [[self _frame] webView];
134 }
135
136 - (WebFrame *)_frame
137 {
138     WebFrameView *webFrameView = [self _web_parentWebFrameView];
139     return [webFrameView webFrame];
140 }
141
142 // Required so view can access the part's selection.
143 - (WebBridge *)_bridge
144 {
145     return [[self _frame] _bridge];
146 }
147
148 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
149 {
150     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
151         location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
152         modifierFlags:[flagsChangedEvent modifierFlags]
153         timestamp:[flagsChangedEvent timestamp]
154         windowNumber:[flagsChangedEvent windowNumber]
155         context:[flagsChangedEvent context]
156         eventNumber:0 clickCount:0 pressure:0];
157
158     // Pretend it's a mouse move.
159     [[NSNotificationCenter defaultCenter]
160         postNotificationName:NSMouseMovedNotification object:self
161         userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
162 }
163
164 - (void)_updateMouseoverWithFakeEvent
165 {
166     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
167         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
168         modifierFlags:[[NSApp currentEvent] modifierFlags]
169         timestamp:[NSDate timeIntervalSinceReferenceDate]
170         windowNumber:[[self window] windowNumber]
171         context:[[NSApp currentEvent] context]
172         eventNumber:0 clickCount:0 pressure:0];
173     
174     [self _updateMouseoverWithEvent:fakeEvent];
175 }
176
177 - (void)_frameOrBoundsChanged
178 {
179     if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
180         [self setNeedsLayout:YES];
181         [self setNeedsDisplay:YES];
182     }
183
184     SEL selector = @selector(_updateMouseoverWithFakeEvent);
185     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:selector object:nil];
186     [self performSelector:selector withObject:nil afterDelay:0];
187 }
188
189 - (NSDictionary *)_elementAtPoint:(NSPoint)point
190 {
191     NSDictionary *elementInfoWC = [[self _bridge] elementAtPoint:point];
192     NSMutableDictionary *elementInfo = [elementInfoWC mutableCopy];
193
194     // Convert URL strings to NSURLs
195     [elementInfo _web_setObjectIfNotNil:[NSURL _web_URLWithDataAsString:[elementInfoWC objectForKey:WebElementLinkURLKey]] forKey:WebElementLinkURLKey];
196     [elementInfo _web_setObjectIfNotNil:[NSURL _web_URLWithDataAsString:[elementInfoWC objectForKey:WebElementImageURLKey]] forKey:WebElementImageURLKey];
197     
198     WebFrameView *webFrameView = [self _web_parentWebFrameView];
199     ASSERT(webFrameView);
200     WebFrame *webFrame = [webFrameView webFrame];
201     
202     if (webFrame) {
203         NSString *frameName = [elementInfoWC objectForKey:WebElementLinkTargetFrameKey];
204         if ([frameName length] == 0) {
205             [elementInfo setObject:webFrame forKey:WebElementLinkTargetFrameKey];
206         } else {
207             WebFrame *wf = [webFrame findFrameNamed:frameName];
208             if (wf != nil)
209                 [elementInfo setObject:wf forKey:WebElementLinkTargetFrameKey];
210             else
211                 [elementInfo removeObjectForKey:WebElementLinkTargetFrameKey];
212         }
213     
214         [elementInfo setObject:webFrame forKey:WebElementFrameKey];
215     }
216     
217     return [elementInfo autorelease];
218 }
219
220 - (void)_setAsideSubviews
221 {
222     ASSERT(!_private->subviewsSetAside);
223     ASSERT(_private->savedSubviews == nil);
224     _private->savedSubviews = _subviews;
225     _subviews = nil;
226     _private->subviewsSetAside = YES;
227  }
228  
229  - (void)_restoreSubviews
230  {
231     ASSERT(_private->subviewsSetAside);
232     ASSERT(_subviews == nil);
233     _subviews = _private->savedSubviews;
234     _private->savedSubviews = nil;
235     _private->subviewsSetAside = NO;
236 }
237
238 // Don't let AppKit even draw subviews. We take care of that.
239 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
240 {
241     // This helps when we print as part of a larger print process.
242     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
243     BOOL wasInPrintingMode = _private->printing;
244     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
245     if (wasInPrintingMode != isPrinting) {
246         if (isPrinting) {
247             [self _web_setPrintingModeRecursive];
248         } else {
249             [self _web_clearPrintingModeRecursive];
250         }
251     }
252
253     [self _web_layoutIfNeededRecursive: rect testDirtyRect:YES];
254
255     [self _setAsideSubviews];
256     [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect
257         rectIsVisibleRectForView:visibleView topView:topView];
258     [self _restoreSubviews];
259
260     if (wasInPrintingMode != isPrinting) {
261         if (wasInPrintingMode) {
262             [self _web_setPrintingModeRecursive];
263         } else {
264             [self _web_clearPrintingModeRecursive];
265         }
266     }
267 }
268
269 // Don't let AppKit even draw subviews. We take care of that.
270 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
271 {
272     BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
273
274     BOOL wasInPrintingMode = _private->printing;
275     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
276
277     if (needToSetAsideSubviews) {
278         // This helps when we print as part of a larger print process.
279         // If the WebHTMLView itself is what we're printing, then we will never have to do this.
280         if (wasInPrintingMode != isPrinting) {
281             if (isPrinting) {
282                 [self _web_setPrintingModeRecursive];
283             } else {
284                 [self _web_clearPrintingModeRecursive];
285             }
286         }
287
288         [self _web_layoutIfNeededRecursive: visRect testDirtyRect:NO];
289
290         [self _setAsideSubviews];
291     }
292     
293     [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
294     
295     if (needToSetAsideSubviews) {
296         if (wasInPrintingMode != isPrinting) {
297             if (wasInPrintingMode) {
298                 [self _web_setPrintingModeRecursive];
299             } else {
300                 [self _web_clearPrintingModeRecursive];
301             }
302         }
303
304         [self _restoreSubviews];
305     }
306 }
307
308 - (BOOL)_insideAnotherHTMLView
309 {
310     NSView *view = self;
311     while ((view = [view superview])) {
312         if ([view isKindOfClass:[WebHTMLView class]]) {
313             return YES;
314         }
315     }
316     return NO;
317 }
318
319 - (void)scrollPoint:(NSPoint)point
320 {
321     // Since we can't subclass NSTextView to do what we want, we have to second guess it here.
322     // If we get called during the handling of a key down event, we assume the call came from
323     // NSTextView, and ignore it and use our own code to decide how to page up and page down
324     // We are smarter about how far to scroll, and we have "superview scrolling" logic.
325     NSEvent *event = [[self window] currentEvent];
326     if ([event type] == NSKeyDown) {
327         const unichar pageUp = NSPageUpFunctionKey;
328         if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageUp length:1]].length == 1) {
329             [self tryToPerform:@selector(scrollPageUp:) with:nil];
330             return;
331         }
332         const unichar pageDown = NSPageDownFunctionKey;
333         if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageDown length:1]].length == 1) {
334             [self tryToPerform:@selector(scrollPageDown:) with:nil];
335             return;
336         }
337     }
338     
339     [super scrollPoint:point];
340     [[self superview] _web_scrollPointToVisible:point fromView: self];
341 }
342
343 - (NSView *)hitTest:(NSPoint)point
344 {
345     // WebHTMLView objects handle all left mouse clicks for objects inside them.
346     // That does not include left mouse clicks with the control key held down.
347     BOOL captureHitsOnSubviews;
348     if (forceRealHitTest) {
349         captureHitsOnSubviews = NO;
350     } else {
351         NSEvent *event = [[self window] currentEvent];
352         captureHitsOnSubviews = [event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) == 0;
353     }
354     if (!captureHitsOnSubviews) {
355         return [super hitTest:point];
356     }
357     if ([[self superview] mouse:point inRect:[self frame]]) {
358         return self;
359     }
360     return nil;
361 }
362
363 static WebHTMLView *lastHitView = nil;
364
365 - (void)_clearLastHitViewIfSelf
366 {
367     if (lastHitView == self) {
368         lastHitView = nil;
369     }
370 }
371
372 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
373 {
374     ASSERT(_private->trackingRectOwner == nil);
375     _private->trackingRectOwner = owner;
376     _private->trackingRectUserData = data;
377     return TRACKING_RECT_TAG;
378 }
379
380 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
381 {
382     ASSERT(tag == TRACKING_RECT_TAG);
383     return [self addTrackingRect:rect owner:owner userData:data assumeInside:assumeInside];
384 }
385
386 - (void)removeTrackingRect:(NSTrackingRectTag)tag
387 {
388     ASSERT(tag == TRACKING_RECT_TAG);
389     if (_private != nil) {
390         ASSERT(_private->trackingRectOwner != nil);
391         _private->trackingRectOwner = nil;
392     }
393 }
394
395 - (void)_sendToolTipMouseExited
396 {
397     // Nothing matters except window, trackingNumber, and userData.
398     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
399         location:NSMakePoint(0, 0)
400         modifierFlags:0
401         timestamp:0
402         windowNumber:[[self window] windowNumber]
403         context:NULL
404         eventNumber:0
405         trackingNumber:TRACKING_RECT_TAG
406         userData:_private->trackingRectUserData];
407     [_private->trackingRectOwner mouseExited:fakeEvent];
408 }
409
410 - (void)_sendToolTipMouseEntered
411 {
412     // Nothing matters except window, trackingNumber, and userData.
413     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
414         location:NSMakePoint(0, 0)
415         modifierFlags:0
416         timestamp:0
417         windowNumber:[[self window] windowNumber]
418         context:NULL
419         eventNumber:0
420         trackingNumber:TRACKING_RECT_TAG
421         userData:_private->trackingRectUserData];
422     [_private->trackingRectOwner mouseEntered:fakeEvent];
423 }
424
425 - (void)_setToolTip:(NSString *)string
426 {
427     NSString *toolTip = [string length] == 0 ? nil : string;
428     NSString *oldToolTip = _private->toolTip;
429     if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
430         return;
431     }
432     if (oldToolTip) {
433         [self _sendToolTipMouseExited];
434         [oldToolTip release];
435     }
436     _private->toolTip = [toolTip copy];
437     if (toolTip) {
438         if (_private->toolTipTag) {
439             [self removeToolTip:_private->toolTipTag];
440         }
441         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
442         _private->toolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
443         [self _sendToolTipMouseEntered];
444     }
445 }
446
447 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
448 {
449     return [[_private->toolTip copy] autorelease];
450 }
451
452 - (void)_updateMouseoverWithEvent:(NSEvent *)event
453 {
454     WebHTMLView *view = nil;
455     if ([event window] == [self window]) {
456         forceRealHitTest = YES;
457         NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
458         forceRealHitTest = NO;
459         while (hitView) {
460             if ([hitView isKindOfClass:[WebHTMLView class]]) {
461                 view = (WebHTMLView *)hitView;
462                 break;
463             }
464             hitView = [hitView superview];
465         }
466     }
467
468     if (lastHitView != view && lastHitView != nil) {
469         // If we are moving out of a view (or frame), let's pretend the mouse moved
470         // all the way out of that view. But we have to account for scrolling, because
471         // khtml doesn't understand our clipping.
472         NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
473         float yScroll = visibleRect.origin.y;
474         float xScroll = visibleRect.origin.x;
475
476         event = [NSEvent mouseEventWithType:NSMouseMoved
477                          location:NSMakePoint(-1 - xScroll, -1 - yScroll )
478                          modifierFlags:[[NSApp currentEvent] modifierFlags]
479                          timestamp:[NSDate timeIntervalSinceReferenceDate]
480                          windowNumber:[[self window] windowNumber]
481                          context:[[NSApp currentEvent] context]
482                          eventNumber:0 clickCount:0 pressure:0];
483         [[lastHitView _bridge] mouseMoved:event];
484     }
485
486     lastHitView = view;
487     
488     NSDictionary *element;
489     if (view == nil) {
490         element = nil;
491     } else {
492         [[view _bridge] mouseMoved:event];
493
494         NSPoint point = [view convertPoint:[event locationInWindow] fromView:nil];
495         element = [view _elementAtPoint:point];
496     }
497
498     // Have the web view send a message to the delegate so it can do status bar display.
499     [[self _webView] _mouseDidMoveOverElement:element modifierFlags:[event modifierFlags]];
500
501     // Set a tool tip; it won't show up right away but will if the user pauses.
502     [self _setToolTip:[element objectForKey:WebCoreElementTitleKey]];
503 }
504
505 + (NSArray *)_pasteboardTypes
506 {
507     return [NSArray arrayWithObjects:
508 #if SUPPORT_HTML_PBOARD
509         NSHTMLPboardType,
510 #endif
511         NSRTFPboardType, NSRTFDPboardType, NSStringPboardType, nil];
512 }
513
514 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
515 {
516     [pasteboard declareTypes:[[self class] _pasteboardTypes] owner:nil];
517
518 #if SUPPORT_HTML_PBOARD
519     // Put HTML on the pasteboard.
520     [pasteboard setData:??? forType:NSHTMLPboardType];
521 #endif
522
523     // Put attributed string on the pasteboard (RTF format).
524     NSAttributedString *attributedString = [self selectedAttributedString];
525     NSRange range = NSMakeRange(0, [attributedString length]);
526     NSData *attributedData = [attributedString RTFFromRange:range documentAttributes:nil];
527     [pasteboard setData:attributedData forType:NSRTFPboardType];
528
529     attributedData = [attributedString RTFDFromRange:range documentAttributes:nil];
530     [pasteboard setData:attributedData forType:NSRTFDPboardType];
531     
532     // Put plain string on the pasteboard.
533     [pasteboard setString:[self selectedString] forType:NSStringPboardType];
534 }
535
536
537 -(NSImage *)_dragImageForLinkElement:(NSDictionary *)element
538 {
539     NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
540
541     BOOL drawURLString = YES;
542     BOOL clipURLString = NO, clipLabelString = NO;
543     
544     NSString *label = [element objectForKey: WebElementLinkLabelKey];
545     NSString *urlString = [linkURL _web_userVisibleString];
546     
547     if (!label) {
548         drawURLString = NO;
549         label = urlString;
550     }
551     
552     NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
553                                                    toHaveTrait:NSBoldFontMask];
554     NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
555     NSSize labelSize;
556     labelSize.width = [label _web_widthWithFont: labelFont];
557     labelSize.height = [labelFont ascender] - [labelFont descender];
558     if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
559         labelSize.width = MAX_DRAG_LABEL_WIDTH;
560         clipLabelString = YES;
561     }
562     
563     NSSize imageSize, urlStringSize;
564     imageSize.width += labelSize.width + DRAG_LABEL_BORDER_X * 2;
565     imageSize.height += labelSize.height + DRAG_LABEL_BORDER_Y * 2;
566     if (drawURLString) {
567         urlStringSize.width = [urlString _web_widthWithFont: urlFont];
568         urlStringSize.height = [urlFont ascender] - [urlFont descender];
569         imageSize.height += urlStringSize.height;
570         if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
571             imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
572             clipURLString = YES;
573         } else {
574             imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2, urlStringSize.width + DRAG_LABEL_BORDER_X * 2);
575         }
576     }
577     NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
578     [dragImage lockFocus];
579     
580     [[NSColor colorWithCalibratedRed: 0.7 green: 0.7 blue: 0.7 alpha: 0.8] set];
581     
582     // Drag a rectangle with rounded corners/
583     NSBezierPath *path = [NSBezierPath bezierPath];
584     [path appendBezierPathWithOvalInRect: NSMakeRect(0,0, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
585     [path appendBezierPathWithOvalInRect: NSMakeRect(0,imageSize.height - DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
586     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2, imageSize.height - DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
587     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2,0, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
588     
589     [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0, imageSize.width - DRAG_LABEL_RADIUS * 2, imageSize.height)];
590     [path appendBezierPathWithRect: NSMakeRect(0, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10, imageSize.height - 2 * DRAG_LABEL_RADIUS)];
591     [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS - 20,DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 20, imageSize.height - 2 * DRAG_LABEL_RADIUS)];
592     [path fill];
593         
594     NSColor *topColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.75];
595     NSColor *bottomColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.5];
596     if (drawURLString) {
597         if (clipURLString)
598             urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2) withFont:urlFont];
599
600         [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender]) 
601              withTopColor:topColor bottomColor:bottomColor font:urlFont];
602     }
603
604     if (clipLabelString)
605             label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2) withFont:labelFont];
606     [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
607              withTopColor:topColor bottomColor:bottomColor font:labelFont];
608     
609     [dragImage unlockFocus];
610     
611     return dragImage;
612 }
613
614 - (void)_handleMouseDragged:(NSEvent *)event
615 {
616     // If the frame has a provisional data source, this view may be released.
617     // Don't allow drag because drag callbacks will reference this released view.
618     if ([[self _frame] provisionalDataSource]) {
619         return;
620     }
621
622     NSPoint mouseDownPoint = [self convertPoint:[_private->mouseDownEvent locationInWindow] fromView:nil];
623     NSDictionary *element = [self _elementAtPoint:mouseDownPoint];
624
625     NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
626     NSURL *imageURL = [element objectForKey:WebElementImageURLKey];
627     BOOL isSelectedText = [[element objectForKey:WebElementIsSelectedKey] boolValue];
628
629     [_private->draggingImageURL release];
630     _private->draggingImageURL = nil;
631
632     // We must have started over something draggable:
633     ASSERT((imageURL && [[WebPreferences standardPreferences] loadsImagesAutomatically]) ||
634            (!imageURL && linkURL) || isSelectedText); 
635
636     NSPoint mouseDraggedPoint = [self convertPoint:[event locationInWindow] fromView:nil];
637     float deltaX = ABS(mouseDraggedPoint.x - mouseDownPoint.x);
638     float deltaY = ABS(mouseDraggedPoint.y - mouseDownPoint.y);
639     
640     // Drag hysteresis hasn't been met yet but we don't want to do other drag actions like selection.
641     if ((imageURL && deltaX < ImageDragHysteresis && deltaY < ImageDragHysteresis) ||
642         (linkURL && deltaX < LinkDragHysteresis && deltaY < LinkDragHysteresis) ||
643         (isSelectedText && deltaX < TextDragHysteresis && deltaY < TextDragHysteresis)) {
644         return;
645     }
646     
647     if (imageURL) {
648         _private->draggingImageURL = [imageURL retain];
649         WebImageRenderer *image = [element objectForKey:WebElementImageKey];
650         ASSERT([image isKindOfClass:[WebImageRenderer class]]);
651         [self _web_dragImage:image
652                  fileWrapper:[[self _webView] _fileWrapperForURL:imageURL]
653                         rect:[[element objectForKey:WebElementImageRectKey] rectValue]
654                          URL:linkURL ? linkURL : imageURL
655                        title:[element objectForKey:WebElementImageAltStringKey]
656                        event:_private->mouseDownEvent];
657         
658     } else if (linkURL) {
659         NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
660         NSString *label = [element objectForKey:WebElementLinkLabelKey];
661         [pasteboard _web_writeURL:linkURL andTitle:label withOwner:self];
662         NSImage *dragImage = [self _dragImageForLinkElement:element];
663         NSSize offset = NSMakeSize([dragImage size].width / 2, -DRAG_LABEL_BORDER_Y);
664         [self dragImage:dragImage
665                      at:NSMakePoint(mouseDraggedPoint.x - offset.width, mouseDraggedPoint.y - offset.height)
666                  offset:offset
667                   event:event
668              pasteboard:pasteboard
669                  source:self
670               slideBack:NO];
671         
672     } else if (isSelectedText) {
673         NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
674         [self _writeSelectionToPasteboard:pasteboard];
675         NSImage *selectionImage = [[self _bridge] selectionImage];
676         [selectionImage _web_dissolveToFraction:WebDragImageAlpha];
677         NSRect visibleSelectionRect = [[self _bridge] visibleSelectionRect];
678         [self dragImage:selectionImage
679                      at:NSMakePoint(NSMinX(visibleSelectionRect), NSMaxY(visibleSelectionRect))
680                  offset:NSMakeSize(mouseDownPoint.x, mouseDownPoint.y)
681                   event:_private->mouseDownEvent
682              pasteboard:pasteboard
683                  source:self
684               slideBack:YES];
685     } else {
686         ERROR("Attempt to drag unknown element");
687     }
688 }
689
690 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
691 {
692     // FIXME: this really needs to be based on a timer
693     [self autoscroll:event];
694 }
695
696 - (BOOL)_mayStartDragWithMouseDragged:(NSEvent *)mouseDraggedEvent
697 {
698     NSPoint mouseDownPoint = [self convertPoint:[_private->mouseDownEvent locationInWindow] fromView:nil];
699     NSDictionary *mouseDownElement = [self _elementAtPoint:mouseDownPoint];
700
701     NSURL *imageURL = [mouseDownElement objectForKey: WebElementImageURLKey];
702
703     if ((imageURL && [[WebPreferences standardPreferences] loadsImagesAutomatically]) ||
704         (!imageURL && [mouseDownElement objectForKey: WebElementLinkURLKey]) ||
705         ([[mouseDownElement objectForKey:WebElementIsSelectedKey] boolValue] &&
706          ([mouseDraggedEvent timestamp] - [_private->mouseDownEvent timestamp]) > TextDragDelay)) {
707         return YES;
708     }
709
710     return NO;
711 }
712
713 - (WebPluginController *)_pluginController
714 {
715     return _private->pluginController;
716 }
717
718 - (void)_web_setPrintingModeRecursive
719 {
720     [self _setPrinting:YES pageWidth:0 adjustViewSize:NO];
721     [super _web_setPrintingModeRecursive];
722 }
723
724 - (void)_web_clearPrintingModeRecursive
725 {
726     [self _setPrinting:NO pageWidth:0 adjustViewSize:NO];
727     [super _web_clearPrintingModeRecursive];
728 }
729
730 - (void)_web_layoutIfNeededRecursive:(NSRect)displayRect testDirtyRect:(bool)testDirtyRect
731 {
732     ASSERT(!_private->subviewsSetAside);
733     displayRect = NSIntersectionRect(displayRect, [self bounds]);
734
735     if (!testDirtyRect || [self needsDisplay]) {
736         if (testDirtyRect) {
737             NSRect dirtyRect = [self _dirtyRect];
738             displayRect = NSIntersectionRect(displayRect, dirtyRect);
739         }
740         if (!NSIsEmptyRect(displayRect)) {
741             if ([[self _bridge] needsLayout])
742                 _private->needsLayout = YES;
743             if (_private->needsToApplyStyles || _private->needsLayout)
744                 [self layout];
745         }
746     }
747
748     [super _web_layoutIfNeededRecursive: displayRect testDirtyRect: NO];
749 }
750
751 - (NSRect)_selectionRect
752 {
753     return [[self _bridge] selectionRect];
754 }
755
756 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
757 {
758     if (_private->autoscrollTimer == nil) {
759         _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
760             target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
761         _private->autoscrollTriggerEvent = [triggerEvent retain];
762     }
763 }
764
765 - (void)_stopAutoscrollTimer
766 {
767     NSTimer *timer = _private->autoscrollTimer;
768     _private->autoscrollTimer = nil;
769     [_private->autoscrollTriggerEvent release];
770     _private->autoscrollTriggerEvent = nil;
771     [timer invalidate];
772     [timer release];
773 }
774
775
776 - (void)_autoscroll
777 {
778     int isStillDown;
779     
780     // Guarantee that the autoscroll timer is invalidated, even if we don't receive
781     // a mouse up event.
782     PSstilldown([_private->autoscrollTriggerEvent eventNumber], &isStillDown);
783     if (!isStillDown){
784         [self _stopAutoscrollTimer];
785         return;
786     }
787
788     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
789         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
790         modifierFlags:[[NSApp currentEvent] modifierFlags]
791         timestamp:[NSDate timeIntervalSinceReferenceDate]
792         windowNumber:[[self window] windowNumber]
793         context:[[NSApp currentEvent] context]
794         eventNumber:0 clickCount:0 pressure:0];
795     [self mouseDragged:fakeEvent];
796 }
797
798 @end
799
800 @implementation NSView (WebHTMLViewPrivate)
801
802 - (void)_web_setPrintingModeRecursive
803 {
804     [_subviews makeObjectsPerformSelector:@selector(_web_setPrintingModeRecursive)];
805 }
806
807 - (void)_web_clearPrintingModeRecursive
808 {
809     [_subviews makeObjectsPerformSelector:@selector(_web_clearPrintingModeRecursive)];
810 }
811
812 - (void)_web_layoutIfNeededRecursive: (NSRect)rect testDirtyRect:(bool)testDirtyRect
813 {
814     unsigned index, count;
815     for (index = 0, count = [_subviews count]; index < count; index++) {
816         NSView *subview = [_subviews objectAtIndex:index];
817         NSRect dirtiedSubviewRect = [subview convertRect: rect fromView: self];
818         [subview _web_layoutIfNeededRecursive: dirtiedSubviewRect testDirtyRect:testDirtyRect];
819     }
820 }
821
822 @end
823
824 @implementation NSMutableDictionary (WebHTMLViewPrivate)
825
826 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
827 {
828     if (object == nil) {
829         [self removeObjectForKey:key];
830     } else {
831         [self setObject:object forKey:key];
832     }
833 }
834
835 @end
836
837 // The following is a workaround for
838 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
839 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
840 // Since the category will be searched before the real class, we'll prevent the flag from being
841 // set on the tool tip panel.
842
843 @interface NSToolTipPanel : NSPanel
844 @end
845
846 @interface NSToolTipPanel (WebHTMLViewPrivate)
847 @end
848
849 @implementation NSToolTipPanel (WebHTMLViewPrivate)
850
851 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
852 {
853     // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
854 }
855
856 @end
857
858
859 @interface WebHTMLView (TextSizing) <_web_WebDocumentTextSizing>
860 @end
861
862 @interface NSArray (WebHTMLView)
863 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
864 @end
865
866 @implementation WebHTMLView
867
868 + (void)initialize
869 {
870     WebKitInitializeUnicode();
871     [NSApp registerServicesMenuSendTypes:[[self class] _pasteboardTypes] returnTypes:nil];
872 }
873
874 - (id)initWithFrame:(NSRect)frame
875 {
876     [super initWithFrame:frame];
877     
878     // Make all drawing go through us instead of subviews.
879     // The bulk of the code to handle this is in WebHTMLViewPrivate.m.
880     if (NSAppKitVersionNumber >= 711) {
881         [self _setDrawsOwnDescendants:YES];
882     }
883     
884     _private = [[WebHTMLViewPrivate alloc] init];
885
886     _private->pluginController = [[WebPluginController alloc] initWithHTMLView:self];
887     _private->needsLayout = YES;
888
889     return self;
890 }
891
892 - (void)dealloc
893 {
894     [self _clearLastHitViewIfSelf];
895     [self _reset];
896     [[NSNotificationCenter defaultCenter] removeObserver:self];
897     [_private release];
898     _private = nil;
899     [super dealloc];
900 }
901
902 - (BOOL)hasSelection
903 {
904     return [[self selectedString] length] != 0;
905 }
906
907 - (IBAction)takeFindStringFromSelection:(id)sender
908 {
909     if (![self hasSelection]) {
910         NSBeep();
911         return;
912     }
913
914     [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
915 }
916
917 - (void)copy:(id)sender
918 {
919     [self _writeSelectionToPasteboard:[NSPasteboard generalPasteboard]];
920 }
921
922 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
923 {
924     [self _writeSelectionToPasteboard:pasteboard];
925     return YES;
926 }
927
928 - (void)selectAll:(id)sender
929 {
930     [self selectAll];
931 }
932
933 - (void)jumpToSelection: sender
934 {
935     [[self _bridge] jumpToSelection];
936 }
937
938
939 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item 
940 {
941     SEL action = [item action];
942     
943     if (action == @selector(copy:))
944         return [self hasSelection];
945     else if (action == @selector(takeFindStringFromSelection:))
946         return [self hasSelection];
947     else if (action == @selector(jumpToSelection:))
948         return [self hasSelection];
949     
950     return YES;
951 }
952
953 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
954 {
955     if (sendType && ([[[self class] _pasteboardTypes] containsObject:sendType]) && [self hasSelection]){
956         return self;
957     }
958
959     return [super validRequestorForSendType:sendType returnType:returnType];
960 }
961
962 - (BOOL)acceptsFirstResponder
963 {
964     // Don't accept first responder when we first click on this view.
965     // We have to pass the event down through WebCore first to be sure we don't hit a subview.
966     // Do accept first responder at any other time, for example from keyboard events,
967     // or from calls back from WebCore once we begin mouse-down event handling.
968     NSEvent *event = [NSApp currentEvent];
969     if ([event type] == NSLeftMouseDown && event != _private->mouseDownEvent && 
970         NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
971         return NO;
972     }
973     return YES;
974 }
975
976 - (void)updateTextBackgroundColor
977 {
978     NSWindow *window = [self window];
979     BOOL shouldUseInactiveTextBackgroundColor = !([window isKeyWindow] && [window firstResponder] == self);
980     WebBridge *bridge = [self _bridge];
981     if ([bridge usesInactiveTextBackgroundColor] != shouldUseInactiveTextBackgroundColor) {
982         [bridge setUsesInactiveTextBackgroundColor:shouldUseInactiveTextBackgroundColor];
983         [self setNeedsDisplayInRect:[bridge visibleSelectionRect]];
984     }
985 }
986
987 - (void)addMouseMovedObserver
988 {
989     if ([[self window] isKeyWindow] && ![self _insideAnotherHTMLView]) {
990         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
991             name:NSMouseMovedNotification object:nil];
992         [self _frameOrBoundsChanged];
993     }
994 }
995
996 - (void)removeMouseMovedObserver
997 {
998     [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
999     [[NSNotificationCenter defaultCenter] removeObserver:self
1000         name:NSMouseMovedNotification object:nil];
1001 }
1002
1003 - (void)updateFocusRing
1004 {
1005     [[self _bridge] setShowsFirstResponder:[[self window] isKeyWindow]];
1006 }
1007
1008 - (void)addSuperviewObservers
1009 {
1010     // We watch the bounds of our superview, so that we can do a layout when the size
1011     // of the superview changes. This is different from other scrollable things that don't
1012     // need this kind of thing because their layout doesn't change.
1013     
1014     // We need to pay attention to both height and width because our "layout" has to change
1015     // to extend the background the full height of the space and because some elements have
1016     // sizes that are based on the total size of the view.
1017     
1018     NSView *superview = [self superview];
1019     if (superview && [self window]) {
1020         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
1021             name:NSViewFrameDidChangeNotification object:superview];
1022         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
1023             name:NSViewBoundsDidChangeNotification object:superview];
1024     }
1025 }
1026
1027 - (void)removeSuperviewObservers
1028 {
1029     NSView *superview = [self superview];
1030     if (superview && [self window]) {
1031         [[NSNotificationCenter defaultCenter] removeObserver:self
1032             name:NSViewFrameDidChangeNotification object:superview];
1033         [[NSNotificationCenter defaultCenter] removeObserver:self
1034             name:NSViewBoundsDidChangeNotification object:superview];
1035     }
1036 }
1037
1038 - (void)addWindowObservers
1039 {
1040     NSWindow *window = [self window];
1041     if (window) {
1042         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:)
1043             name:NSWindowDidBecomeKeyNotification object:window];
1044         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:)
1045             name:NSWindowDidResignKeyNotification object:window];
1046         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:)
1047             name:NSWindowWillCloseNotification object:window];
1048     }
1049 }
1050
1051 - (void)removeWindowObservers
1052 {
1053     NSWindow *window = [self window];
1054     if (window) {
1055         [[NSNotificationCenter defaultCenter] removeObserver:self
1056             name:NSWindowDidBecomeKeyNotification object:window];
1057         [[NSNotificationCenter defaultCenter] removeObserver:self
1058             name:NSWindowDidResignKeyNotification object:window];
1059         [[NSNotificationCenter defaultCenter] removeObserver:self
1060             name:NSWindowWillCloseNotification object:window];
1061     }
1062 }
1063
1064 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
1065 {
1066     [self removeSuperviewObservers];
1067 }
1068
1069 - (void)viewDidMoveToSuperview
1070 {
1071     // Do this here in case the text size multiplier changed when a non-HTML
1072     // view was installed.
1073     if ([self superview] != nil) {
1074         [self _updateTextSizeMultiplier];
1075         [self addSuperviewObservers];
1076     }
1077 }
1078
1079 - (void)viewWillMoveToWindow:(NSWindow *)window
1080 {
1081     // Don't do anything if we aren't initialized.  This happens
1082     // when decoding a WebView.  When WebViews are decoded their subviews
1083     // are created by initWithCoder: and so won't be normally
1084     // initialized.  The stub views are discarded by WebView.
1085     if (_private){
1086         // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
1087         [self removeMouseMovedObserver];
1088         [self removeWindowObservers];
1089         [self removeSuperviewObservers];
1090         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
1091     
1092         [[self _pluginController] stopAllPlugins];
1093     }
1094 }
1095
1096 - (void)viewDidMoveToWindow
1097 {
1098     // Don't do anything if we aren't initialized.  This happens
1099     // when decoding a WebView.  When WebViews are decoded their subviews
1100     // are created by initWithCoder: and so won't be normally
1101     // initialized.  The stub views are discarded by WebView.
1102     if (_private) {
1103         [self _stopAutoscrollTimer];
1104         if ([self window]) {
1105             [self addWindowObservers];
1106             [self addSuperviewObservers];
1107             [self addMouseMovedObserver];
1108             [self updateTextBackgroundColor];
1109     
1110             [[self _pluginController] startAllPlugins];
1111     
1112             _private->inWindow = YES;
1113         } else {
1114             // Reset when we are moved out of a window after being moved into one.
1115             // Without this check, we reset ourselves before we even start.
1116             // This is only needed because viewDidMoveToWindow is called even when
1117             // the window is not changing (bug in AppKit).
1118             if (_private->inWindow) {
1119                 [self _reset];
1120                 _private->inWindow = NO;
1121             }
1122         }
1123     }
1124 }
1125
1126 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1127 {
1128     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
1129 }
1130
1131 - (void)viewDidMoveToHostWindow
1132 {
1133     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
1134 }
1135
1136
1137 - (void)addSubview:(NSView *)view
1138 {
1139     if ([view conformsToProtocol:@protocol(WebPlugin)]) {
1140         [[self _pluginController] addPlugin:view];
1141     }
1142
1143     [super addSubview:view];
1144 }
1145
1146 - (void)reapplyStyles
1147 {
1148     if (!_private->needsToApplyStyles) {
1149         return;
1150     }
1151     
1152 #ifdef _KWQ_TIMING        
1153     double start = CFAbsoluteTimeGetCurrent();
1154 #endif
1155
1156     [[self _bridge] reapplyStylesForDeviceType:
1157         _private->printing ? WebCoreDevicePrinter : WebCoreDeviceScreen];
1158     
1159 #ifdef _KWQ_TIMING        
1160     double thisTime = CFAbsoluteTimeGetCurrent() - start;
1161     LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
1162 #endif
1163
1164     _private->needsToApplyStyles = NO;
1165 }
1166
1167 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
1168 // pageWidth==0 implies a non-printing layout
1169 - (void)layoutToPageWidth:(float)pageWidth adjustingViewSize:(BOOL)adjustViewSize
1170 {
1171     [self reapplyStyles];
1172     
1173     // Ensure that we will receive mouse move events.  Is this the best place to put this?
1174     [[self window] setAcceptsMouseMovedEvents: YES];
1175     [[self window] _setShouldPostEventNotifications: YES];
1176
1177     if (!_private->needsLayout) {
1178         return;
1179     }
1180
1181 #ifdef _KWQ_TIMING        
1182     double start = CFAbsoluteTimeGetCurrent();
1183 #endif
1184
1185     LOG(View, "%@ doing layout", self);
1186
1187     if (pageWidth > 0.0) {
1188         [[self _bridge] forceLayoutForPageWidth:pageWidth adjustingViewSize:adjustViewSize];
1189     } else {
1190         [[self _bridge] forceLayoutAdjustingViewSize:adjustViewSize];
1191     }
1192     _private->needsLayout = NO;
1193     
1194     if (!_private->printing) {
1195         // get size of the containing dynamic scrollview, so
1196         // appearance and disappearance of scrollbars will not show up
1197         // as a size change
1198         NSSize newLayoutFrameSize = [[[self superview] superview] frame].size;
1199         if (_private->laidOutAtLeastOnce && !NSEqualSizes(_private->lastLayoutFrameSize, newLayoutFrameSize)) {
1200             [[self _bridge] sendResizeEvent];
1201         }
1202         _private->laidOutAtLeastOnce = YES;
1203         _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
1204         _private->lastLayoutFrameSize = newLayoutFrameSize;
1205     }
1206
1207 #ifdef _KWQ_TIMING        
1208     double thisTime = CFAbsoluteTimeGetCurrent() - start;
1209     LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
1210 #endif
1211 }
1212
1213 - (void)layout
1214 {
1215     [self layoutToPageWidth:0.0 adjustingViewSize:NO];
1216 }
1217
1218 - (NSMenu *)menuForEvent:(NSEvent *)event
1219 {
1220     if ([[self _bridge] sendContextMenuEvent:event]) {
1221         return nil;
1222     }
1223     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
1224     NSDictionary *element = [self _elementAtPoint:point];
1225     return [[self _webView] _menuForElement:element];
1226 }
1227
1228 // Search from the end of the currently selected location, or from the beginning of the
1229 // document if nothing is selected.
1230 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag;
1231 {
1232     return [[self _bridge] searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag];
1233 }
1234
1235 - (NSString *)string
1236 {
1237     return [[self attributedString] string];
1238 }
1239
1240 - (NSAttributedString *)attributedString
1241 {
1242     WebBridge *b = [self _bridge];
1243     return [b attributedStringFrom:[b DOMDocument]
1244                        startOffset:0
1245                                 to:nil
1246                          endOffset:0];
1247 }
1248
1249 - (NSString *)selectedString
1250 {
1251     return [[self _bridge] selectedString];
1252 }
1253
1254 // Get an attributed string that represents the current selection.
1255 - (NSAttributedString *)selectedAttributedString
1256 {
1257     return [[self _bridge] selectedAttributedString];
1258 }
1259
1260 - (void)selectAll
1261 {
1262     [[self _bridge] selectAll];
1263 }
1264
1265 // Remove the selection.
1266 - (void)deselectAll
1267 {
1268     [[self _bridge] deselectAll];
1269 }
1270
1271 - (void)deselectText
1272 {
1273     [[self _bridge] deselectText];
1274 }
1275
1276 - (BOOL)isOpaque
1277 {
1278     return YES;
1279 }
1280
1281 - (void)setNeedsDisplay:(BOOL)flag
1282 {
1283     LOG(View, "%@ flag = %d", self, (int)flag);
1284     [super setNeedsDisplay: flag];
1285 }
1286
1287 - (void)setNeedsLayout: (BOOL)flag
1288 {
1289     LOG(View, "%@ flag = %d", self, (int)flag);
1290     _private->needsLayout = flag;
1291 }
1292
1293
1294 - (void)setNeedsToApplyStyles: (BOOL)flag
1295 {
1296     LOG(View, "%@ flag = %d", self, (int)flag);
1297     _private->needsToApplyStyles = flag;
1298 }
1299
1300 - (void)drawRect:(NSRect)rect
1301 {
1302     LOG(View, "%@ drawing", self);
1303     
1304     BOOL subviewsWereSetAside = _private->subviewsSetAside;
1305     if (subviewsWereSetAside) {
1306         [self _restoreSubviews];
1307     }
1308
1309 #ifdef _KWQ_TIMING
1310     double start = CFAbsoluteTimeGetCurrent();
1311 #endif
1312
1313     [NSGraphicsContext saveGraphicsState];
1314     NSRectClip(rect);
1315         
1316     ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
1317
1318     [(WebClipView *)[self superview] setAdditionalClip:rect];
1319
1320     NS_DURING {
1321         WebTextRendererFactory *textRendererFactoryIfCoalescing = nil;
1322         if ([WebTextRenderer shouldBufferTextDrawing] && [NSView focusView]) {
1323             textRendererFactoryIfCoalescing = [WebTextRendererFactory sharedFactory];
1324             [textRendererFactoryIfCoalescing startCoalesceTextDrawing];
1325         }
1326
1327         //double start = CFAbsoluteTimeGetCurrent();
1328         [[self _bridge] drawRect:rect];
1329         //LOG(Timing, "draw time %e", CFAbsoluteTimeGetCurrent() - start);
1330
1331         if (textRendererFactoryIfCoalescing != nil) {
1332             [textRendererFactoryIfCoalescing endCoalesceTextDrawing];
1333         }
1334
1335         [(WebClipView *)[self superview] resetAdditionalClip];
1336
1337         [NSGraphicsContext restoreGraphicsState];
1338     } NS_HANDLER {
1339         [(WebClipView *)[self superview] resetAdditionalClip];
1340         [NSGraphicsContext restoreGraphicsState];
1341         ERROR("Exception caught while drawing: %@", localException);
1342         [localException raise];
1343     } NS_ENDHANDLER
1344
1345 #ifdef DEBUG_LAYOUT
1346     NSRect vframe = [self frame];
1347     [[NSColor blackColor] set];
1348     NSBezierPath *path;
1349     path = [NSBezierPath bezierPath];
1350     [path setLineWidth:(float)0.1];
1351     [path moveToPoint:NSMakePoint(0, 0)];
1352     [path lineToPoint:NSMakePoint(vframe.size.width, vframe.size.height)];
1353     [path closePath];
1354     [path stroke];
1355     path = [NSBezierPath bezierPath];
1356     [path setLineWidth:(float)0.1];
1357     [path moveToPoint:NSMakePoint(0, vframe.size.height)];
1358     [path lineToPoint:NSMakePoint(vframe.size.width, 0)];
1359     [path closePath];
1360     [path stroke];
1361 #endif
1362
1363 #ifdef _KWQ_TIMING
1364     double thisTime = CFAbsoluteTimeGetCurrent() - start;
1365     LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
1366 #endif
1367
1368     if (subviewsWereSetAside) {
1369         [self _setAsideSubviews];
1370     }
1371 }
1372
1373 // Turn off the additional clip while computing our visibleRect.
1374 - (NSRect)visibleRect
1375 {
1376     if (!([[self superview] isKindOfClass:[WebClipView class]]))
1377         return [super visibleRect];
1378         
1379     WebClipView *clipView = (WebClipView *)[self superview];
1380
1381     BOOL hasAdditionalClip = [clipView hasAdditionalClip];
1382     if (!hasAdditionalClip) {
1383         return [super visibleRect];
1384     }
1385     
1386     NSRect additionalClip = [clipView additionalClip];
1387     [clipView resetAdditionalClip];
1388     NSRect visibleRect = [super visibleRect];
1389     [clipView setAdditionalClip:additionalClip];
1390     return visibleRect;
1391 }
1392
1393 - (BOOL)isFlipped 
1394 {
1395     return YES;
1396 }
1397
1398 - (void)windowDidBecomeKey:(NSNotification *)notification
1399 {
1400     ASSERT([notification object] == [self window]);
1401     [self addMouseMovedObserver];
1402     [self updateTextBackgroundColor];
1403     [self updateFocusRing];
1404 }
1405
1406 - (void)windowDidResignKey: (NSNotification *)notification
1407 {
1408     ASSERT([notification object] == [self window]);
1409     [self removeMouseMovedObserver];
1410     [self updateTextBackgroundColor];
1411     [self updateFocusRing];
1412 }
1413
1414 - (void)windowWillClose:(NSNotification *)notification
1415 {
1416     [[self _pluginController] destroyAllPlugins];
1417 }
1418
1419 - (BOOL)_isSelectionEvent:(NSEvent *)event
1420 {
1421     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
1422     return [[[self _elementAtPoint:point] objectForKey:WebElementIsSelectedKey] boolValue];
1423 }
1424
1425 - (BOOL)acceptsFirstMouse:(NSEvent *)event
1426 {
1427     return [self _isSelectionEvent:event];
1428 }
1429
1430 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
1431 {
1432     return [self _isSelectionEvent:event];
1433 }
1434
1435 - (void)mouseDown:(NSEvent *)event
1436 {
1437     // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
1438     // We don't want to pass them along to KHTML a second time.
1439     if ([event modifierFlags] & NSControlKeyMask) {
1440         return;
1441     }
1442     
1443     _private->ignoringMouseDraggedEvents = NO;
1444     [self _startAutoscrollTimer:event];
1445     
1446     // Record the mouse down position so we can determine drag hysteresis.
1447     [_private->mouseDownEvent release];
1448     _private->mouseDownEvent = [event retain];
1449
1450     // Don't do any mouseover while the mouse is down.
1451     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
1452
1453     // Let KHTML get a chance to deal with the event.
1454     [[self _bridge] mouseDown:event];
1455 }
1456
1457 - (void)dragImage:(NSImage *)dragImage
1458                at:(NSPoint)at
1459            offset:(NSSize)offset
1460             event:(NSEvent *)event
1461        pasteboard:(NSPasteboard *)pasteboard
1462            source:(id)source
1463         slideBack:(BOOL)slideBack
1464 {    
1465     [self _stopAutoscrollTimer];
1466
1467     // Don't allow drags to be accepted by this WebFrameView.
1468     [[self _webView] unregisterDraggedTypes];
1469     
1470     // Retain this view during the drag because it may be released before the drag ends.
1471     [self retain];
1472
1473     [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
1474 }
1475
1476 - (void)mouseDragged:(NSEvent *)event
1477 {
1478     if (!_private->ignoringMouseDraggedEvents) {
1479         [[self _bridge] mouseDragged:event];
1480     }
1481 }
1482
1483 - (unsigned)draggingSourceOperationMaskForLocal:(BOOL)isLocal
1484 {
1485     return (NSDragOperationGeneric|NSDragOperationCopy);
1486 }
1487
1488 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
1489 {
1490     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
1491     _private->ignoringMouseDraggedEvents = YES;
1492     
1493     // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
1494     // khtml expects to get balanced down/up's, so we must fake up a mouseup.
1495     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
1496                                             location:[[self window] convertScreenToBase:aPoint]
1497                                        modifierFlags:[[NSApp currentEvent] modifierFlags]
1498                                            timestamp:[NSDate timeIntervalSinceReferenceDate]
1499                                         windowNumber:[[self window] windowNumber]
1500                                              context:[[NSApp currentEvent] context]
1501                                          eventNumber:0 clickCount:0 pressure:0];
1502     [self mouseUp:fakeEvent];       // This will also update the mouseover state.
1503
1504     // Reregister for drag types because they were unregistered before the drag.
1505     [[self _webView] _registerDraggedTypes];
1506     
1507     // Balance the previous retain from when the drag started.
1508     [self release];
1509 }
1510
1511 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
1512 {
1513     ASSERT(_private->draggingImageURL);
1514     
1515     WebView *webView = [self _webView];
1516     NSFileWrapper *wrapper = [webView _fileWrapperForURL:_private->draggingImageURL];
1517     NSString *filename;
1518     
1519     if (wrapper) {
1520         // FIXME: Report an error if we fail to create a file.
1521         NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
1522         path = [[NSFileManager defaultManager] _web_pathWithUniqueFilenameForPath:path];
1523         if (![wrapper writeToFile:path atomically:NO updateFilenames:YES]) {
1524             ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
1525         }
1526         filename = [path lastPathComponent];
1527     } else {
1528         // FIXME: The file is supposed to be created at this point so the Finder places the file
1529         // where the drag ended. Since we can't create the file until the download starts,
1530         // this fails.
1531         [webView _downloadURL:_private->draggingImageURL toDirectory:[dropDestination path]];
1532         filename = [_private->draggingImageURL _web_suggestedFilenameWithMIMEType:nil];
1533     }
1534     
1535     return [NSArray arrayWithObject:filename];
1536 }
1537
1538 - (void)mouseUp:(NSEvent *)event
1539 {
1540     [self _stopAutoscrollTimer];
1541     [[self _bridge] mouseUp:event];
1542     [self _updateMouseoverWithFakeEvent];
1543 }
1544
1545 - (void)mouseMovedNotification:(NSNotification *)notification
1546 {
1547     [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
1548 }
1549
1550 - (BOOL)supportsTextEncoding
1551 {
1552     return YES;
1553 }
1554
1555 - (NSView *)nextKeyView
1556 {
1557     return (_private && _private->inNextValidKeyView && ![[self _bridge] inNextKeyViewOutsideWebFrameViews])
1558         ? [[self _bridge] nextKeyView]
1559         : [super nextKeyView];
1560 }
1561
1562 - (NSView *)previousKeyView
1563 {
1564     return (_private && _private->inNextValidKeyView)
1565         ? [[self _bridge] previousKeyView]
1566         : [super previousKeyView];
1567 }
1568
1569 - (NSView *)nextValidKeyView
1570 {
1571     _private->inNextValidKeyView = YES;
1572     NSView *view = [super nextValidKeyView];
1573     _private->inNextValidKeyView = NO;
1574     return view;
1575 }
1576
1577 - (NSView *)previousValidKeyView
1578 {
1579     _private->inNextValidKeyView = YES;
1580     NSView *view = [super previousValidKeyView];
1581     _private->inNextValidKeyView = NO;
1582     return view;
1583 }
1584
1585 - (BOOL)becomeFirstResponder
1586 {
1587     NSView *view = nil;
1588     if (![[self _webView] _isPerformingProgrammaticFocus]) {
1589         switch ([[self window] keyViewSelectionDirection]) {
1590         case NSDirectSelection:
1591             break;
1592         case NSSelectingNext:
1593             view = [[self _bridge] nextKeyViewInsideWebFrameViews];
1594             break;
1595         case NSSelectingPrevious:
1596             view = [[self _bridge] previousKeyViewInsideWebFrameViews];
1597             break;
1598         }
1599     }
1600     if (view) {
1601         [[self window] makeFirstResponder:view];
1602     }
1603     [self updateTextBackgroundColor];
1604     return YES;
1605 }
1606
1607 // This approach could be relaxed when dealing with 3228554
1608 - (BOOL)resignFirstResponder
1609 {
1610     BOOL resign = [super resignFirstResponder];
1611     if (resign) {
1612         if ([[self _webView] _isPerformingProgrammaticFocus]) {
1613             [self deselectText];
1614         }
1615         else {
1616             [self deselectAll];
1617         }
1618         [self updateTextBackgroundColor];
1619     }
1620     return resign;
1621 }
1622
1623 //------------------------------------------------------------------------------------
1624 // WebDocumentView protocol
1625 //------------------------------------------------------------------------------------
1626 - (void)setDataSource:(WebDataSource *)dataSource 
1627 {
1628 }
1629
1630 - (void)dataSourceUpdated:(WebDataSource *)dataSource
1631 {
1632 }
1633
1634 // Does setNeedsDisplay:NO as a side effect. Useful for begin/endDocument.
1635 // pageWidth != 0 implies we will relayout to a new width
1636 - (void)_setPrinting:(BOOL)printing pageWidth:(float)pageWidth adjustViewSize:(BOOL)adjustViewSize
1637 {
1638     WebFrame *frame = [self _frame];
1639     NSArray *subframes = [frame childFrames];
1640     unsigned n = [subframes count];
1641     unsigned i;
1642     for (i = 0; i != n; ++i) {
1643         WebFrame *subframe = [subframes objectAtIndex:i];
1644         WebFrameView *frameView = [subframe frameView];
1645         if ([[subframe dataSource] _isDocumentHTML]) {
1646             [(WebHTMLView *)[frameView documentView] _setPrinting:printing pageWidth:0 adjustViewSize:adjustViewSize];
1647         }
1648     }
1649
1650     if (printing != _private->printing) {
1651         _private->printing = printing;
1652         [self setNeedsToApplyStyles:YES];
1653         [self setNeedsLayout:YES];
1654         [self layoutToPageWidth:pageWidth adjustingViewSize:adjustViewSize];
1655         [self setNeedsDisplay:NO];
1656     }
1657 }
1658
1659 - (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit
1660 {
1661     // This helps when we print as part of a larger print process.
1662     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1663     BOOL wasInPrintingMode = _private->printing;
1664     if (!wasInPrintingMode) {
1665         [self _setPrinting:YES pageWidth:0 adjustViewSize:NO];
1666     }
1667     
1668     [[self _bridge] adjustPageHeightNew:newBottom top:oldTop bottom:oldBottom limit:bottomLimit];
1669
1670     if (!wasInPrintingMode) {
1671         [self _setPrinting:NO pageWidth:0 adjustViewSize:NO];
1672     }
1673 }
1674
1675 - (void)beginDocument
1676 {
1677     // Must do this explicit display here, because otherwise the view might redisplay while the print
1678     // sheet was up, using printer fonts (and looking different).
1679     [self displayIfNeeded];
1680     [[self window] setAutodisplay:NO];
1681
1682     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
1683     // according to the paper size
1684     float pageWidth = 0.0;
1685     if (![[self _bridge] isFrameSet]) {
1686         NSPrintInfo *printInfo = [[NSPrintOperation currentOperation] printInfo];
1687         pageWidth = ([printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin])*PrintingExtraWidthFactor;
1688     }
1689     [self _setPrinting:YES pageWidth:pageWidth adjustViewSize:YES];     // will relayout
1690
1691     [super beginDocument];
1692     // There is a theoretical chance that someone could do some drawing between here and endDocument,
1693     // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
1694     // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
1695 }
1696
1697 - (void)endDocument
1698 {
1699     [super endDocument];
1700     // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO 
1701     [self _setPrinting:NO pageWidth:0.0 adjustViewSize:YES];
1702     [[self window] setAutodisplay:YES];
1703 }
1704
1705 - (void)_updateTextSizeMultiplier
1706 {
1707     [[self _bridge] setTextSizeMultiplier:[[self _webView] textSizeMultiplier]];    
1708 }
1709
1710 - (void)keyDown:(NSEvent *)event
1711 {
1712     BOOL intercepted = [[self _bridge] interceptKeyEvent:event toView:self];
1713     if (!intercepted || [event _web_isTabKeyEvent]) {
1714         [super keyDown:event];
1715     }
1716 }
1717
1718 - (void)keyUp:(NSEvent *)event
1719 {
1720     if (![[self _bridge] interceptKeyEvent:event toView:self]) {
1721         [super keyUp:event];
1722     }
1723 }
1724
1725 - (id)accessibilityAttributeValue:(NSString*)attributeName
1726 {
1727     if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
1728         id accTree = [[self _bridge] accessibilityTree];
1729         if (accTree)
1730             return [NSArray arrayWithObject: accTree];
1731         return nil;
1732     }
1733     return [super accessibilityAttributeValue:attributeName];
1734 }
1735
1736 - (id)accessibilityHitTest:(NSPoint)point
1737 {
1738     id accTree = [[self _bridge] accessibilityTree];
1739     if (accTree) {
1740         NSPoint windowCoord = [[self window] convertScreenToBase: point];
1741         return [accTree accessibilityHitTest: [self convertPoint:windowCoord fromView:nil]];
1742     }
1743     else
1744         return self;
1745 }
1746 @end
1747
1748 @implementation WebHTMLView (TextSizing)
1749
1750 - (void)_web_textSizeMultiplierChanged
1751 {
1752     [self _updateTextSizeMultiplier];
1753 }
1754
1755 @end
1756
1757 @implementation NSArray (WebHTMLView)
1758
1759 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object
1760 {
1761     NSEnumerator *enumerator = [self objectEnumerator];
1762     WebNetscapePluginEmbeddedView *view;
1763     while ((view = [enumerator nextObject]) != nil) {
1764         if ([view isKindOfClass:[WebNetscapePluginEmbeddedView class]]) {
1765             [view performSelector:selector withObject:object];
1766         }
1767     }
1768 }
1769
1770 @end