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