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