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