3 Copyright 2002, Apple, Inc. All rights reserved.
6 #import <WebKit/WebHTMLView.h>
8 #import <WebKit/WebBridge.h>
9 #import <WebKit/WebClipView.h>
10 #import <WebKit/WebDataSourcePrivate.h>
11 #import <WebKit/WebDocumentInternal.h>
12 #import <WebKit/WebDOMDocument.h>
13 #import <WebKit/WebException.h>
14 #import <WebKit/WebFrame.h>
15 #import <WebKit/WebFramePrivate.h>
16 #import <WebKit/WebFrameViewPrivate.h>
17 #import <WebKit/WebHTMLViewPrivate.h>
18 #import <WebKit/WebNetscapePluginEmbeddedView.h>
19 #import <WebKit/WebKitLogging.h>
20 #import <WebKit/WebNSPasteboardExtras.h>
21 #import <WebKit/WebNSViewExtras.h>
22 #import <WebKit/WebPluginController.h>
23 #import <WebKit/WebTextRenderer.h>
24 #import <WebKit/WebTextRendererFactory.h>
25 #import <WebKit/WebUnicode.h>
26 #import <WebKit/WebViewPrivate.h>
28 #import <AppKit/NSResponder_Private.h>
29 #import <CoreGraphics/CGContextGState.h>
31 #import <AppKit/NSAccessibility.h>
33 #import <WebKit/WebImageRenderer.h>
34 #import <WebKit/WebKitNSStringExtras.h>
35 #import <WebKit/WebNSImageExtras.h>
36 #import <WebKit/WebNSURLExtras.h>
37 #import <WebKit/WebPreferences.h>
38 #import <WebKit/WebStringTruncator.h>
41 // These are a little larger than typical because dragging links is a fairly
42 // advanced feature that can confuse non-power-users
43 #define DragHysteresis 10.0
44 #define TextDragHysteresis 3.0
45 #define TextDragDelay 0.15
47 #define AUTOSCROLL_INTERVAL 0.1
49 #define DRAG_LABEL_BORDER_X 4.0
50 #define DRAG_LABEL_BORDER_Y 2.0
51 #define DRAG_LABEL_RADIUS 5.0
52 #define DRAG_LABEL_BORDER_Y_OFFSET 2.0
54 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120.0
55 #define MAX_DRAG_LABEL_WIDTH 320.0
57 #define DRAG_LINK_LABEL_FONT_SIZE 11.0
58 #define DRAG_LINK_URL_FONT_SIZE 10.0
60 static BOOL forceRealHitTest = NO;
62 @interface WebHTMLView (WebHTMLViewPrivate)
63 - (void)_setPrinting:(BOOL)printing pageWidth:(float)pageWidth adjustViewSize:(BOOL)adjustViewSize;
64 - (void)_updateTextSizeMultiplier;
67 // Any non-zero value will do, but using somethign recognizable might help us debug some day.
68 #define TRACKING_RECT_TAG 0xBADFACE
71 @interface NSView (AppKitSecretsIKnowAbout)
72 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView testDirtyRect:(BOOL)testDirtyRect;
73 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
75 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
78 @interface NSView (WebHTMLViewPrivate)
79 - (void)_web_setPrintingModeRecursive;
80 - (void)_web_clearPrintingModeRecursive;
81 - (void)_web_layoutIfNeededRecursive:(NSRect)rect testDirtyRect:(bool)testDirtyRect;
84 @interface NSMutableDictionary (WebHTMLViewPrivate)
85 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
88 @implementation WebHTMLViewPrivate
92 ASSERT(autoscrollTimer == nil);
94 [pluginController destroyAllPlugins];
96 [mouseDownEvent release];
97 [draggingImageURL release];
98 [pluginController release];
107 @implementation WebHTMLView (WebPrivate)
109 - (void)_adjustFrames
111 // Ick! khtml set the frame size during layout and
112 // the frame origins during drawing! So we have to
113 // layout and do a draw with rendering disabled to
114 // correclty adjust the frames.
115 [[self _bridge] adjustFrames:[self frame]];
120 [WebImageRenderer stopAnimationsInView:self];
123 - (WebView *)_webView
125 // We used to use the view hierarchy exclusively here, but that won't work
126 // right when the first viewDidMoveToSuperview call is done, and this wil.
127 return [[self _frame] webView];
132 WebFrameView *webFrameView = [self _web_parentWebFrameView];
133 return [webFrameView webFrame];
136 // Required so view can access the part's selection.
137 - (WebBridge *)_bridge
139 return [[self _frame] _bridge];
142 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
144 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
145 location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
146 modifierFlags:[flagsChangedEvent modifierFlags]
147 timestamp:[flagsChangedEvent timestamp]
148 windowNumber:[flagsChangedEvent windowNumber]
149 context:[flagsChangedEvent context]
150 eventNumber:0 clickCount:0 pressure:0];
152 // Pretend it's a mouse move.
153 [[NSNotificationCenter defaultCenter]
154 postNotificationName:NSMouseMovedNotification object:self
155 userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
158 - (void)_updateMouseoverWithFakeEvent
160 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
161 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
162 modifierFlags:[[NSApp currentEvent] modifierFlags]
163 timestamp:[NSDate timeIntervalSinceReferenceDate]
164 windowNumber:[[self window] windowNumber]
165 context:[[NSApp currentEvent] context]
166 eventNumber:0 clickCount:0 pressure:0];
168 [self _updateMouseoverWithEvent:fakeEvent];
171 - (void)_frameOrBoundsChanged
173 if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
174 [self setNeedsLayout:YES];
175 [self setNeedsDisplay:YES];
178 SEL selector = @selector(_updateMouseoverWithFakeEvent);
179 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:selector object:nil];
180 [self performSelector:selector withObject:nil afterDelay:0];
183 - (NSDictionary *)_elementAtPoint:(NSPoint)point
185 NSDictionary *elementInfoWC = [[self _bridge] elementAtPoint:point];
186 NSMutableDictionary *elementInfo = [elementInfoWC mutableCopy];
188 // Convert URL strings to NSURLs
189 [elementInfo _web_setObjectIfNotNil:[NSURL _web_URLWithDataAsString:[elementInfoWC objectForKey:WebElementLinkURLKey]] forKey:WebElementLinkURLKey];
190 [elementInfo _web_setObjectIfNotNil:[NSURL _web_URLWithDataAsString:[elementInfoWC objectForKey:WebElementImageURLKey]] forKey:WebElementImageURLKey];
192 WebFrameView *webFrameView = [self _web_parentWebFrameView];
193 ASSERT(webFrameView);
194 WebFrame *webFrame = [webFrameView webFrame];
197 NSString *frameName = [elementInfoWC objectForKey:WebElementLinkTargetFrameKey];
198 if ([frameName length] == 0) {
199 [elementInfo setObject:webFrame forKey:WebElementLinkTargetFrameKey];
201 WebFrame *wf = [webFrame findFrameNamed:frameName];
203 [elementInfo setObject:wf forKey:WebElementLinkTargetFrameKey];
205 [elementInfo removeObjectForKey:WebElementLinkTargetFrameKey];
208 [elementInfo setObject:webFrame forKey:WebElementFrameKey];
211 return [elementInfo autorelease];
214 - (void)_setAsideSubviews
216 ASSERT(!_private->subviewsSetAside);
217 ASSERT(_private->savedSubviews == nil);
218 _private->savedSubviews = _subviews;
220 _private->subviewsSetAside = YES;
223 - (void)_restoreSubviews
225 ASSERT(_private->subviewsSetAside);
226 ASSERT(_subviews == nil);
227 _subviews = _private->savedSubviews;
228 _private->savedSubviews = nil;
229 _private->subviewsSetAside = NO;
232 // Don't let AppKit even draw subviews. We take care of that.
233 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView testDirtyRect:(BOOL)testDirtyRect
235 // This helps when we print as part of a larger print process.
236 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
237 BOOL wasInPrintingMode = _private->printing;
238 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
239 if (wasInPrintingMode != isPrinting) {
241 [self _web_setPrintingModeRecursive];
243 [self _web_clearPrintingModeRecursive];
247 [self _web_layoutIfNeededRecursive: rect testDirtyRect:YES];
249 [self _setAsideSubviews];
250 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect
251 rectIsVisibleRectForView:visibleView testDirtyRect:testDirtyRect];
252 [self _restoreSubviews];
254 if (wasInPrintingMode != isPrinting) {
255 if (wasInPrintingMode) {
256 [self _web_setPrintingModeRecursive];
258 [self _web_clearPrintingModeRecursive];
263 // Don't let AppKit even draw subviews. We take care of that.
264 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
266 BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
268 BOOL wasInPrintingMode = _private->printing;
269 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
271 if (needToSetAsideSubviews) {
272 // This helps when we print as part of a larger print process.
273 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
274 if (wasInPrintingMode != isPrinting) {
276 [self _web_setPrintingModeRecursive];
278 [self _web_clearPrintingModeRecursive];
282 [self _web_layoutIfNeededRecursive: visRect testDirtyRect:NO];
284 [self _setAsideSubviews];
287 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
289 if (needToSetAsideSubviews) {
290 if (wasInPrintingMode != isPrinting) {
291 if (wasInPrintingMode) {
292 [self _web_setPrintingModeRecursive];
294 [self _web_clearPrintingModeRecursive];
298 [self _restoreSubviews];
302 - (BOOL)_insideAnotherHTMLView
305 while ((view = [view superview])) {
306 if ([view isKindOfClass:[WebHTMLView class]]) {
313 - (void)scrollPoint:(NSPoint)point
315 // Since we can't subclass NSTextView to do what we want, we have to second guess it here.
316 // If we get called during the handling of a key down event, we assume the call came from
317 // NSTextView, and ignore it and use our own code to decide how to page up and page down
318 // We are smarter about how far to scroll, and we have "superview scrolling" logic.
319 NSEvent *event = [[self window] currentEvent];
320 if ([event type] == NSKeyDown) {
321 const unichar pageUp = NSPageUpFunctionKey;
322 if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageUp length:1]].length == 1) {
323 [self tryToPerform:@selector(scrollPageUp:) with:nil];
326 const unichar pageDown = NSPageDownFunctionKey;
327 if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageDown length:1]].length == 1) {
328 [self tryToPerform:@selector(scrollPageDown:) with:nil];
333 [super scrollPoint:point];
336 - (NSView *)hitTest:(NSPoint)point
338 // WebHTMLView objects handle all left mouse clicks for objects inside them.
339 // That does not include left mouse clicks with the control key held down.
340 BOOL captureHitsOnSubviews;
341 if (forceRealHitTest) {
342 captureHitsOnSubviews = NO;
344 NSEvent *event = [[self window] currentEvent];
345 captureHitsOnSubviews = [event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) == 0;
347 if (!captureHitsOnSubviews) {
348 return [super hitTest:point];
350 if ([[self superview] mouse:point inRect:[self frame]]) {
356 static WebHTMLView *lastHitView = nil;
358 - (void)_clearLastHitViewIfSelf
360 if (lastHitView == self) {
365 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
367 ASSERT(_private->trackingRectOwner == nil);
368 _private->trackingRectOwner = owner;
369 _private->trackingRectUserData = data;
370 return TRACKING_RECT_TAG;
373 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
375 ASSERT(tag == TRACKING_RECT_TAG);
376 return [self addTrackingRect:rect owner:owner userData:data assumeInside:assumeInside];
379 - (void)removeTrackingRect:(NSTrackingRectTag)tag
381 ASSERT(tag == TRACKING_RECT_TAG);
382 if (_private != nil) {
383 ASSERT(_private->trackingRectOwner != nil);
384 _private->trackingRectOwner = nil;
388 - (void)_sendToolTipMouseExited
390 // Nothing matters except window, trackingNumber, and userData.
391 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
392 location:NSMakePoint(0, 0)
395 windowNumber:[[self window] windowNumber]
398 trackingNumber:TRACKING_RECT_TAG
399 userData:_private->trackingRectUserData];
400 [_private->trackingRectOwner mouseExited:fakeEvent];
403 - (void)_sendToolTipMouseEntered
405 // Nothing matters except window, trackingNumber, and userData.
406 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
407 location:NSMakePoint(0, 0)
410 windowNumber:[[self window] windowNumber]
413 trackingNumber:TRACKING_RECT_TAG
414 userData:_private->trackingRectUserData];
415 [_private->trackingRectOwner mouseEntered:fakeEvent];
418 - (void)_setToolTip:(NSString *)string
420 NSString *toolTip = [string length] == 0 ? nil : string;
421 NSString *oldToolTip = _private->toolTip;
422 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
426 [self _sendToolTipMouseExited];
427 [oldToolTip release];
429 _private->toolTip = [toolTip copy];
431 if (_private->toolTipTag) {
432 [self removeToolTip:_private->toolTipTag];
434 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
435 _private->toolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
436 [self _sendToolTipMouseEntered];
440 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
442 return [[_private->toolTip copy] autorelease];
445 - (void)_updateMouseoverWithEvent:(NSEvent *)event
447 WebHTMLView *view = nil;
448 if ([event window] == [self window]) {
449 forceRealHitTest = YES;
450 NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
451 forceRealHitTest = NO;
453 if ([hitView isKindOfClass:[WebHTMLView class]]) {
454 view = (WebHTMLView *)hitView;
457 hitView = [hitView superview];
461 if (lastHitView != view && lastHitView != nil) {
462 // If we are moving out of a view (or frame), let's pretend the mouse moved
463 // all the way out of that view. But we have to account for scrolling, because
464 // khtml doesn't understand our clipping.
465 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
466 float yScroll = visibleRect.origin.y;
467 float xScroll = visibleRect.origin.x;
469 event = [NSEvent mouseEventWithType:NSMouseMoved
470 location:NSMakePoint(-1 - xScroll, -1 - yScroll )
471 modifierFlags:[[NSApp currentEvent] modifierFlags]
472 timestamp:[NSDate timeIntervalSinceReferenceDate]
473 windowNumber:[[self window] windowNumber]
474 context:[[NSApp currentEvent] context]
475 eventNumber:0 clickCount:0 pressure:0];
476 [[lastHitView _bridge] mouseMoved:event];
481 NSDictionary *element;
485 [[view _bridge] mouseMoved:event];
487 NSPoint point = [view convertPoint:[event locationInWindow] fromView:nil];
488 element = [view _elementAtPoint:point];
491 // Have the web view send a message to the delegate so it can do status bar display.
492 [[self _webView] _mouseDidMoveOverElement:element modifierFlags:[event modifierFlags]];
494 // Set a tool tip; it won't show up right away but will if the user pauses.
495 [self _setToolTip:[element objectForKey:WebCoreElementTitleKey]];
498 + (NSArray *)_pasteboardTypes
500 return [NSArray arrayWithObjects:
501 #if SUPPORT_HTML_PBOARD
504 NSRTFPboardType, NSRTFDPboardType, NSStringPboardType, nil];
507 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
509 [pasteboard declareTypes:[[self class] _pasteboardTypes] owner:nil];
511 #if SUPPORT_HTML_PBOARD
512 // Put HTML on the pasteboard.
513 [pasteboard setData:??? forType:NSHTMLPboardType];
516 // Put attributed string on the pasteboard (RTF format).
517 NSAttributedString *attributedString = [self selectedAttributedString];
518 NSRange range = NSMakeRange(0, [attributedString length]);
519 NSData *attributedData = [attributedString RTFFromRange:range documentAttributes:nil];
520 [pasteboard setData:attributedData forType:NSRTFPboardType];
522 attributedData = [attributedString RTFDFromRange:range documentAttributes:nil];
523 [pasteboard setData:attributedData forType:NSRTFDPboardType];
525 // Put plain string on the pasteboard.
526 [pasteboard setString:[self selectedString] forType:NSStringPboardType];
530 -(NSImage *)_dragImageForLinkElement:(NSDictionary *)element
532 NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
534 BOOL drawURLString = YES;
535 BOOL clipURLString = NO, clipLabelString = NO;
537 NSString *label = [element objectForKey: WebElementLinkLabelKey];
538 NSString *urlString = [linkURL _web_userVisibleString];
545 NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
546 toHaveTrait:NSBoldFontMask];
547 NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
549 labelSize.width = [label _web_widthWithFont: labelFont];
550 labelSize.height = [labelFont ascender] - [labelFont descender];
551 if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
552 labelSize.width = MAX_DRAG_LABEL_WIDTH;
553 clipLabelString = YES;
556 NSSize imageSize, urlStringSize;
557 imageSize.width += labelSize.width + DRAG_LABEL_BORDER_X * 2;
558 imageSize.height += labelSize.height + DRAG_LABEL_BORDER_Y * 2;
560 urlStringSize.width = [urlString _web_widthWithFont: urlFont];
561 urlStringSize.height = [urlFont ascender] - [urlFont descender];
562 imageSize.height += urlStringSize.height;
563 if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
564 imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
567 imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2, urlStringSize.width + DRAG_LABEL_BORDER_X * 2);
570 NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
571 [dragImage lockFocus];
573 [[NSColor colorWithCalibratedRed: 0.7 green: 0.7 blue: 0.7 alpha: 0.8] set];
575 // Drag a rectangle with rounded corners/
576 NSBezierPath *path = [NSBezierPath bezierPath];
577 [path appendBezierPathWithOvalInRect: NSMakeRect(0,0, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
578 [path appendBezierPathWithOvalInRect: NSMakeRect(0,imageSize.height - DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
579 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2, imageSize.height - DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
580 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2,0, DRAG_LABEL_RADIUS * 2, DRAG_LABEL_RADIUS * 2)];
582 [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0, imageSize.width - DRAG_LABEL_RADIUS * 2, imageSize.height)];
583 [path appendBezierPathWithRect: NSMakeRect(0, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10, imageSize.height - 2 * DRAG_LABEL_RADIUS)];
584 [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS - 20,DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 20, imageSize.height - 2 * DRAG_LABEL_RADIUS)];
587 NSColor *topColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.75];
588 NSColor *bottomColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.5];
591 urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2) withFont:urlFont];
593 [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender])
594 withTopColor:topColor bottomColor:bottomColor font:urlFont];
598 label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2) withFont:labelFont];
599 [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
600 withTopColor:topColor bottomColor:bottomColor font:labelFont];
602 [dragImage unlockFocus];
607 - (void)_handleMouseDragged:(NSEvent *)event
609 // If the frame has a provisional data source, this view may be released.
610 // Don't allow drag because drag callbacks will reference this released view.
611 if ([[self _frame] provisionalDataSource]) {
615 NSPoint mouseDownPoint = [self convertPoint:[_private->mouseDownEvent locationInWindow] fromView:nil];
616 NSDictionary *element = [self _elementAtPoint:mouseDownPoint];
618 NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
619 NSURL *imageURL = [element objectForKey:WebElementImageURLKey];
620 BOOL isSelectedText = [[element objectForKey:WebElementIsSelectedKey] boolValue];
622 [_private->draggingImageURL release];
623 _private->draggingImageURL = nil;
625 // We must have started over something draggable:
626 ASSERT((imageURL && [[WebPreferences standardPreferences] loadsImagesAutomatically]) ||
627 (!imageURL && linkURL) || isSelectedText);
629 NSPoint mouseDraggedPoint = [self convertPoint:[event locationInWindow] fromView:nil];
630 float deltaX = ABS(mouseDraggedPoint.x - mouseDownPoint.x);
631 float deltaY = ABS(mouseDraggedPoint.y - mouseDownPoint.y);
633 // Drag hysteresis hasn't been met yet but we don't want to do other drag actions like selection.
634 if (((imageURL || linkURL) && deltaX < DragHysteresis && deltaY < DragHysteresis) ||
635 (isSelectedText && deltaX < TextDragHysteresis && deltaY < TextDragHysteresis)) {
640 _private->draggingImageURL = [imageURL retain];
641 WebImageRenderer *image = [element objectForKey:WebElementImageKey];
642 ASSERT([image isKindOfClass:[WebImageRenderer class]]);
643 [self _web_dragPromisedImage:image
644 rect:[[element objectForKey:WebElementImageRectKey] rectValue]
645 URL:linkURL ? linkURL : imageURL
646 title:[element objectForKey:WebElementImageAltStringKey]
647 event:_private->mouseDownEvent];
649 } else if (linkURL) {
650 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
651 NSString *label = [element objectForKey:WebElementLinkLabelKey];
652 [pasteboard _web_writeURL:linkURL andTitle:label withOwner:self];
653 NSImage *dragImage = [self _dragImageForLinkElement:element];
654 NSSize offset = NSMakeSize([dragImage size].width / 2, -DRAG_LABEL_BORDER_Y);
655 [self dragImage:dragImage
656 at:NSMakePoint(mouseDraggedPoint.x - offset.width, mouseDraggedPoint.y - offset.height)
659 pasteboard:pasteboard
663 } else if (isSelectedText) {
664 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
665 [self _writeSelectionToPasteboard:pasteboard];
666 NSImage *selectionImage = [[self _bridge] selectionImage];
667 [selectionImage _web_dissolveToFraction:WebDragImageAlpha];
668 NSRect visibleSelectionRect = [[self _bridge] visibleSelectionRect];
669 [self dragImage:selectionImage
670 at:NSMakePoint(NSMinX(visibleSelectionRect), NSMaxY(visibleSelectionRect))
671 offset:NSMakeSize(mouseDownPoint.x, mouseDownPoint.y)
672 event:_private->mouseDownEvent
673 pasteboard:pasteboard
677 ERROR("Attempt to drag unknown element");
681 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
683 // FIXME: this really needs to be based on a timer
684 [self autoscroll:event];
687 - (BOOL)_mayStartDragWithMouseDragged:(NSEvent *)mouseDraggedEvent
689 NSPoint mouseDownPoint = [self convertPoint:[_private->mouseDownEvent locationInWindow] fromView:nil];
690 NSDictionary *mouseDownElement = [self _elementAtPoint:mouseDownPoint];
692 NSURL *imageURL = [mouseDownElement objectForKey: WebElementImageURLKey];
694 if ((imageURL && [[WebPreferences standardPreferences] loadsImagesAutomatically]) ||
695 (!imageURL && [mouseDownElement objectForKey: WebElementLinkURLKey]) ||
696 ([[mouseDownElement objectForKey:WebElementIsSelectedKey] boolValue] &&
697 ([mouseDraggedEvent timestamp] - [_private->mouseDownEvent timestamp]) > TextDragDelay)) {
704 - (WebPluginController *)_pluginController
706 return _private->pluginController;
709 - (void)_web_setPrintingModeRecursive
711 [self _setPrinting:YES pageWidth:0 adjustViewSize:NO];
712 [super _web_setPrintingModeRecursive];
715 - (void)_web_clearPrintingModeRecursive
717 [self _setPrinting:NO pageWidth:0 adjustViewSize:NO];
718 [super _web_clearPrintingModeRecursive];
721 - (void)_web_layoutIfNeededRecursive:(NSRect)displayRect testDirtyRect:(bool)testDirtyRect
723 ASSERT(!_private->subviewsSetAside);
724 displayRect = NSIntersectionRect(displayRect, [self bounds]);
726 if (!testDirtyRect || [self needsDisplay]) {
728 NSRect dirtyRect = [self _dirtyRect];
729 displayRect = NSIntersectionRect(displayRect, dirtyRect);
731 if (!NSIsEmptyRect(displayRect)) {
732 if ([[self _bridge] needsLayout])
733 _private->needsLayout = YES;
734 if (_private->needsToApplyStyles || _private->needsLayout)
739 [super _web_layoutIfNeededRecursive: displayRect testDirtyRect: NO];
742 - (NSRect)_selectionRect
744 return [[self _bridge] selectionRect];
747 - (void)_startAutoscrollTimer
749 if (_private->autoscrollTimer == nil) {
750 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
751 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
755 - (void)_stopAutoscrollTimer
757 NSTimer *timer = _private->autoscrollTimer;
758 _private->autoscrollTimer = nil;
765 NSEvent *mouseUpEvent = [NSApp nextEventMatchingMask:NSLeftMouseUpMask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:NO];
770 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
771 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
772 modifierFlags:[[NSApp currentEvent] modifierFlags]
773 timestamp:[NSDate timeIntervalSinceReferenceDate]
774 windowNumber:[[self window] windowNumber]
775 context:[[NSApp currentEvent] context]
776 eventNumber:0 clickCount:0 pressure:0];
777 [self mouseDragged:fakeEvent];
782 @implementation NSView (WebHTMLViewPrivate)
784 - (void)_web_setPrintingModeRecursive
786 [_subviews makeObjectsPerformSelector:@selector(_web_setPrintingModeRecursive)];
789 - (void)_web_clearPrintingModeRecursive
791 [_subviews makeObjectsPerformSelector:@selector(_web_clearPrintingModeRecursive)];
794 - (void)_web_layoutIfNeededRecursive: (NSRect)rect testDirtyRect:(bool)testDirtyRect
796 unsigned index, count;
797 for (index = 0, count = [_subviews count]; index < count; index++) {
798 NSView *subview = [_subviews objectAtIndex:index];
799 NSRect dirtiedSubviewRect = [subview convertRect: rect fromView: self];
800 [subview _web_layoutIfNeededRecursive: dirtiedSubviewRect testDirtyRect:testDirtyRect];
806 @implementation NSMutableDictionary (WebHTMLViewPrivate)
808 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
811 [self removeObjectForKey:key];
813 [self setObject:object forKey:key];
819 // The following is a workaround for
820 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
821 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
822 // Since the category will be searched before the real class, we'll prevent the flag from being
823 // set on the tool tip panel.
825 @interface NSToolTipPanel : NSPanel
828 @interface NSToolTipPanel (WebHTMLViewPrivate)
831 @implementation NSToolTipPanel (WebHTMLViewPrivate)
833 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
835 // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
841 @interface WebHTMLView (TextSizing) <_web_WebDocumentTextSizing>
844 @interface NSArray (WebHTMLView)
845 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
848 @implementation WebHTMLView
852 WebKitInitializeUnicode();
853 [NSApp registerServicesMenuSendTypes:[[self class] _pasteboardTypes] returnTypes:nil];
856 - (id)initWithFrame:(NSRect)frame
858 [super initWithFrame:frame];
860 // Make all drawing go through us instead of subviews.
861 // The bulk of the code to handle this is in WebHTMLViewPrivate.m.
862 if (NSAppKitVersionNumber >= 711) {
863 [self _setDrawsOwnDescendants:YES];
866 _private = [[WebHTMLViewPrivate alloc] init];
868 _private->pluginController = [[WebPluginController alloc] initWithHTMLView:self];
869 _private->needsLayout = YES;
876 [self _clearLastHitViewIfSelf];
878 [[NSNotificationCenter defaultCenter] removeObserver:self];
886 return [[self selectedString] length] != 0;
889 - (IBAction)takeFindStringFromSelection:(id)sender
891 if (![self hasSelection]) {
896 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
899 - (void)copy:(id)sender
901 [self _writeSelectionToPasteboard:[NSPasteboard generalPasteboard]];
904 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
906 [self _writeSelectionToPasteboard:pasteboard];
910 - (void)selectAll:(id)sender
915 - (void)jumpToSelection: sender
917 [[self _bridge] jumpToSelection];
921 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
923 SEL action = [item action];
925 if (action == @selector(copy:))
926 return [self hasSelection];
927 else if (action == @selector(takeFindStringFromSelection:))
928 return [self hasSelection];
929 else if (action == @selector(jumpToSelection:))
930 return [self hasSelection];
935 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
937 if (sendType && ([[[self class] _pasteboardTypes] containsObject:sendType]) && [self hasSelection]){
941 return [super validRequestorForSendType:sendType returnType:returnType];
944 - (BOOL)acceptsFirstResponder
946 // Don't accept first responder when we first click on this view.
947 // We have to pass the event down through WebCore first to be sure we don't hit a subview.
948 // Do accept first responder at any other time, for example from keyboard events,
949 // or from calls back from WebCore once we begin mouse-down event handling.
950 NSEvent *event = [NSApp currentEvent];
951 if ([event type] == NSLeftMouseDown && event != _private->mouseDownEvent) {
957 - (void)updateTextBackgroundColor
959 NSWindow *window = [self window];
960 BOOL shouldUseInactiveTextBackgroundColor = !([window isKeyWindow] && [window firstResponder] == self);
961 WebBridge *bridge = [self _bridge];
962 if ([bridge usesInactiveTextBackgroundColor] != shouldUseInactiveTextBackgroundColor) {
963 [bridge setUsesInactiveTextBackgroundColor:shouldUseInactiveTextBackgroundColor];
964 [self setNeedsDisplayInRect:[bridge visibleSelectionRect]];
968 - (void)addMouseMovedObserver
970 if ([[self window] isKeyWindow] && ![self _insideAnotherHTMLView]) {
971 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
972 name:NSMouseMovedNotification object:nil];
973 [self _frameOrBoundsChanged];
977 - (void)removeMouseMovedObserver
979 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
980 [[NSNotificationCenter defaultCenter] removeObserver:self
981 name:NSMouseMovedNotification object:nil];
984 - (void)addSuperviewObservers
986 // We watch the bounds of our superview, so that we can do a layout when the size
987 // of the superview changes. This is different from other scrollable things that don't
988 // need this kind of thing because their layout doesn't change.
990 // We need to pay attention to both height and width because our "layout" has to change
991 // to extend the background the full height of the space and because some elements have
992 // sizes that are based on the total size of the view.
994 NSView *superview = [self superview];
995 if (superview && [self window]) {
996 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged)
997 name:NSViewFrameDidChangeNotification object:superview];
998 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged)
999 name:NSViewBoundsDidChangeNotification object:superview];
1003 - (void)removeSuperviewObservers
1005 NSView *superview = [self superview];
1006 if (superview && [self window]) {
1007 [[NSNotificationCenter defaultCenter] removeObserver:self
1008 name:NSViewFrameDidChangeNotification object:superview];
1009 [[NSNotificationCenter defaultCenter] removeObserver:self
1010 name:NSViewBoundsDidChangeNotification object:superview];
1014 - (void)addWindowObservers
1016 NSWindow *window = [self window];
1018 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:)
1019 name:NSWindowDidBecomeKeyNotification object:window];
1020 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:)
1021 name:NSWindowDidResignKeyNotification object:window];
1022 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:)
1023 name:NSWindowWillCloseNotification object:window];
1027 - (void)removeWindowObservers
1029 NSWindow *window = [self window];
1031 [[NSNotificationCenter defaultCenter] removeObserver:self
1032 name:NSWindowDidBecomeKeyNotification object:window];
1033 [[NSNotificationCenter defaultCenter] removeObserver:self
1034 name:NSWindowDidResignKeyNotification object:window];
1035 [[NSNotificationCenter defaultCenter] removeObserver:self
1036 name:NSWindowWillCloseNotification object:window];
1040 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
1042 [self removeSuperviewObservers];
1045 - (void)viewDidMoveToSuperview
1047 // Do this here in case the text size multiplier changed when a non-HTML
1048 // view was installed.
1049 if ([self superview] != nil) {
1050 [self _updateTextSizeMultiplier];
1051 [self addSuperviewObservers];
1055 - (void)viewWillMoveToWindow:(NSWindow *)window
1057 // Don't do anything if we aren't initialized. This happens
1058 // when decoding a WebView. When WebViews are decoded their subviews
1059 // are created by initWithCoder: and so won't be normally
1060 // initialized. The stub views are discarded by WebView.
1062 // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
1063 [self removeMouseMovedObserver];
1064 [self removeWindowObservers];
1065 [self removeSuperviewObservers];
1066 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
1068 [[self _pluginController] stopAllPlugins];
1072 - (void)viewDidMoveToWindow
1074 // Don't do anything if we aren't initialized. This happens
1075 // when decoding a WebView. When WebViews are decoded their subviews
1076 // are created by initWithCoder: and so won't be normally
1077 // initialized. The stub views are discarded by WebView.
1079 [self _stopAutoscrollTimer];
1080 if ([self window]) {
1081 [self addWindowObservers];
1082 [self addSuperviewObservers];
1083 [self addMouseMovedObserver];
1084 [self updateTextBackgroundColor];
1086 [[self _pluginController] startAllPlugins];
1088 _private->inWindow = YES;
1090 // Reset when we are moved out of a window after being moved into one.
1091 // Without this check, we reset ourselves before we even start.
1092 // This is only needed because viewDidMoveToWindow is called even when
1093 // the window is not changing (bug in AppKit).
1094 if (_private->inWindow) {
1096 _private->inWindow = NO;
1102 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1104 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
1107 - (void)viewDidMoveToHostWindow
1109 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
1113 - (void)addSubview:(NSView *)view
1115 if ([view conformsToProtocol:@protocol(WebPlugin)]) {
1116 [[self _pluginController] addPlugin:view];
1119 [super addSubview:view];
1122 - (void)reapplyStyles
1124 if (!_private->needsToApplyStyles) {
1129 double start = CFAbsoluteTimeGetCurrent();
1132 [[self _bridge] reapplyStylesForDeviceType:
1133 _private->printing ? WebCoreDevicePrinter : WebCoreDeviceScreen];
1136 double thisTime = CFAbsoluteTimeGetCurrent() - start;
1137 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
1140 _private->needsToApplyStyles = NO;
1143 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
1144 // pageWidth==0 implies a non-printing layout
1145 - (void)layoutToPageWidth:(float)pageWidth adjustingViewSize:(BOOL)adjustViewSize
1147 [self reapplyStyles];
1149 // Ensure that we will receive mouse move events. Is this the best place to put this?
1150 [[self window] setAcceptsMouseMovedEvents: YES];
1151 [[self window] _setShouldPostEventNotifications: YES];
1153 if (!_private->needsLayout) {
1158 double start = CFAbsoluteTimeGetCurrent();
1161 LOG(View, "%@ doing layout", self);
1163 if (pageWidth > 0.0) {
1164 [[self _bridge] forceLayoutForPageWidth:pageWidth adjustingViewSize:adjustViewSize];
1166 [[self _bridge] forceLayoutAdjustingViewSize:adjustViewSize];
1168 _private->needsLayout = NO;
1170 if (!_private->printing) {
1171 // get size of the containing dynamic scrollview, so
1172 // appearance and disappearance of scrollbars will not show up
1174 NSSize newLayoutFrameSize = [[[self superview] superview] frame].size;
1175 if (_private->laidOutAtLeastOnce && !NSEqualSizes(_private->lastLayoutFrameSize, newLayoutFrameSize)) {
1176 [[self _bridge] sendResizeEvent];
1178 _private->laidOutAtLeastOnce = YES;
1179 _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
1180 _private->lastLayoutFrameSize = newLayoutFrameSize;
1184 double thisTime = CFAbsoluteTimeGetCurrent() - start;
1185 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
1191 [self layoutToPageWidth:0.0 adjustingViewSize:NO];
1194 - (NSMenu *)menuForEvent:(NSEvent *)event
1196 if ([[self _bridge] sendContextMenuEvent:event]) {
1199 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
1200 NSDictionary *element = [self _elementAtPoint:point];
1201 return [[self _webView] _menuForElement:element];
1204 // Search from the end of the currently selected location, or from the beginning of the
1205 // document if nothing is selected.
1206 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag;
1208 return [[self _bridge] searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag];
1211 - (NSString *)string
1213 return [[self attributedString] string];
1216 - (NSAttributedString *)attributedString
1218 WebBridge *b = [self _bridge];
1219 return [b attributedStringFrom:[b DOMDocument]
1225 - (NSString *)selectedString
1227 return [[self _bridge] selectedString];
1230 // Get an attributed string that represents the current selection.
1231 - (NSAttributedString *)selectedAttributedString
1233 return [[self _bridge] selectedAttributedString];
1238 [[self _bridge] selectAll];
1241 // Remove the selection.
1244 [[self _bridge] deselectAll];
1252 - (void)setNeedsDisplay:(BOOL)flag
1254 LOG(View, "%@ flag = %d", self, (int)flag);
1255 [super setNeedsDisplay: flag];
1258 - (void)setNeedsLayout: (BOOL)flag
1260 LOG(View, "%@ flag = %d", self, (int)flag);
1261 _private->needsLayout = flag;
1265 - (void)setNeedsToApplyStyles: (BOOL)flag
1267 LOG(View, "%@ flag = %d", self, (int)flag);
1268 _private->needsToApplyStyles = flag;
1271 - (void)drawRect:(NSRect)rect
1273 LOG(View, "%@ drawing", self);
1275 BOOL subviewsWereSetAside = _private->subviewsSetAside;
1276 if (subviewsWereSetAside) {
1277 [self _restoreSubviews];
1281 double start = CFAbsoluteTimeGetCurrent();
1284 [NSGraphicsContext saveGraphicsState];
1287 ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
1289 [(WebClipView *)[self superview] setAdditionalClip:rect];
1292 WebTextRendererFactory *textRendererFactoryIfCoalescing = nil;
1293 if ([WebTextRenderer shouldBufferTextDrawing] && [NSView focusView]) {
1294 textRendererFactoryIfCoalescing = [WebTextRendererFactory sharedFactory];
1295 [textRendererFactoryIfCoalescing startCoalesceTextDrawing];
1298 //double start = CFAbsoluteTimeGetCurrent();
1299 [[self _bridge] drawRect:rect];
1300 //LOG(Timing, "draw time %e", CFAbsoluteTimeGetCurrent() - start);
1302 if (textRendererFactoryIfCoalescing != nil) {
1303 [textRendererFactoryIfCoalescing endCoalesceTextDrawing];
1306 [(WebClipView *)[self superview] resetAdditionalClip];
1308 [NSGraphicsContext restoreGraphicsState];
1310 [(WebClipView *)[self superview] resetAdditionalClip];
1311 [NSGraphicsContext restoreGraphicsState];
1312 ERROR("Exception caught while drawing: %@", localException);
1313 [localException raise];
1317 NSRect vframe = [self frame];
1318 [[NSColor blackColor] set];
1320 path = [NSBezierPath bezierPath];
1321 [path setLineWidth:(float)0.1];
1322 [path moveToPoint:NSMakePoint(0, 0)];
1323 [path lineToPoint:NSMakePoint(vframe.size.width, vframe.size.height)];
1326 path = [NSBezierPath bezierPath];
1327 [path setLineWidth:(float)0.1];
1328 [path moveToPoint:NSMakePoint(0, vframe.size.height)];
1329 [path lineToPoint:NSMakePoint(vframe.size.width, 0)];
1335 double thisTime = CFAbsoluteTimeGetCurrent() - start;
1336 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
1339 if (subviewsWereSetAside) {
1340 [self _setAsideSubviews];
1344 // Turn off the additional clip while computing our visibleRect.
1345 - (NSRect)visibleRect
1347 if (!([[self superview] isKindOfClass:[WebClipView class]]))
1348 return [super visibleRect];
1350 WebClipView *clipView = (WebClipView *)[self superview];
1352 BOOL hasAdditionalClip = [clipView hasAdditionalClip];
1353 if (!hasAdditionalClip) {
1354 return [super visibleRect];
1357 NSRect additionalClip = [clipView additionalClip];
1358 [clipView resetAdditionalClip];
1359 NSRect visibleRect = [super visibleRect];
1360 [clipView setAdditionalClip:additionalClip];
1369 - (void)windowDidBecomeKey:(NSNotification *)notification
1371 ASSERT([notification object] == [self window]);
1372 [self addMouseMovedObserver];
1373 [self updateTextBackgroundColor];
1376 - (void)windowDidResignKey: (NSNotification *)notification
1378 ASSERT([notification object] == [self window]);
1379 [self removeMouseMovedObserver];
1380 [self updateTextBackgroundColor];
1383 - (void)windowWillClose:(NSNotification *)notification
1385 [[self _pluginController] destroyAllPlugins];
1388 - (BOOL)_isSelectionEvent:(NSEvent *)event
1390 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
1391 return [[[self _elementAtPoint:point] objectForKey:WebElementIsSelectedKey] boolValue];
1394 - (BOOL)acceptsFirstMouse:(NSEvent *)event
1396 return [self _isSelectionEvent:event];
1399 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
1401 return [self _isSelectionEvent:event];
1404 - (void)mouseDown:(NSEvent *)event
1406 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
1407 // We don't want to pass them along to KHTML a second time.
1408 if ([event modifierFlags] & NSControlKeyMask) {
1412 _private->ignoringMouseDraggedEvents = NO;
1413 [self _startAutoscrollTimer];
1415 // Record the mouse down position so we can determine drag hysteresis.
1416 [_private->mouseDownEvent release];
1417 _private->mouseDownEvent = [event retain];
1419 // Don't do any mouseover while the mouse is down.
1420 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
1422 // Let KHTML get a chance to deal with the event.
1423 [[self _bridge] mouseDown:event];
1426 - (void)dragImage:(NSImage *)dragImage
1428 offset:(NSSize)offset
1429 event:(NSEvent *)event
1430 pasteboard:(NSPasteboard *)pasteboard
1432 slideBack:(BOOL)slideBack
1434 [self _stopAutoscrollTimer];
1436 // Don't allow drags to be accepted by this WebFrameView.
1437 [[self _webView] unregisterDraggedTypes];
1439 // Retain this view during the drag because it may be released before the drag ends.
1442 [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
1445 - (void)mouseDragged:(NSEvent *)event
1447 if (!_private->ignoringMouseDraggedEvents) {
1448 [[self _bridge] mouseDragged:event];
1452 - (unsigned)draggingSourceOperationMaskForLocal:(BOOL)isLocal
1454 return (NSDragOperationGeneric|NSDragOperationCopy);
1457 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
1459 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
1460 _private->ignoringMouseDraggedEvents = YES;
1462 // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
1463 // khtml expects to get balanced down/up's, so we must fake up a mouseup.
1464 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
1465 location:[[self window] convertScreenToBase:aPoint]
1466 modifierFlags:[[NSApp currentEvent] modifierFlags]
1467 timestamp:[NSDate timeIntervalSinceReferenceDate]
1468 windowNumber:[[self window] windowNumber]
1469 context:[[NSApp currentEvent] context]
1470 eventNumber:0 clickCount:0 pressure:0];
1471 [self mouseUp:fakeEvent]; // This will also update the mouseover state.
1473 // Reregister for drag types because they were unregistered before the drag.
1474 [[self _webView] _registerDraggedTypes];
1476 // Balance the previous retain from when the drag started.
1480 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
1482 if (!_private->draggingImageURL) {
1486 [[self _webView] _downloadURL:_private->draggingImageURL toDirectory:[dropDestination path]];
1488 // FIXME: The file is supposed to be created at this point so the Finder places the file
1489 // where the drag ended. Since we can't create the file until the download starts,
1490 // this fails. Even if we did create the file at this point, the Finder doesn't
1491 // place the file in the right place anyway (2825055).
1492 // FIXME: We may return a different filename than the file that we will create.
1493 // Since the file isn't created at this point anwyway, it doesn't matter what we return.
1494 return [NSArray arrayWithObject:[[_private->draggingImageURL path] lastPathComponent]];
1497 - (void)mouseUp:(NSEvent *)event
1499 [self _stopAutoscrollTimer];
1500 [[self _bridge] mouseUp:event];
1501 [self _updateMouseoverWithFakeEvent];
1504 - (void)mouseMovedNotification:(NSNotification *)notification
1506 [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
1509 - (BOOL)supportsTextEncoding
1514 - (NSView *)nextKeyView
1516 return (_private && _private->inNextValidKeyView && ![[self _bridge] inNextKeyViewOutsideWebFrameViews])
1517 ? [[self _bridge] nextKeyView]
1518 : [super nextKeyView];
1521 - (NSView *)previousKeyView
1523 return (_private && _private->inNextValidKeyView)
1524 ? [[self _bridge] previousKeyView]
1525 : [super previousKeyView];
1528 - (NSView *)nextValidKeyView
1530 _private->inNextValidKeyView = YES;
1531 NSView *view = [super nextValidKeyView];
1532 _private->inNextValidKeyView = NO;
1536 - (NSView *)previousValidKeyView
1538 _private->inNextValidKeyView = YES;
1539 NSView *view = [super previousValidKeyView];
1540 _private->inNextValidKeyView = NO;
1544 - (BOOL)becomeFirstResponder
1547 if (![[self _webView] _isPerformingProgrammaticFocus]) {
1548 switch ([[self window] keyViewSelectionDirection]) {
1549 case NSDirectSelection:
1551 case NSSelectingNext:
1552 view = [[self _bridge] nextKeyViewInsideWebFrameViews];
1554 case NSSelectingPrevious:
1555 view = [[self _bridge] previousKeyViewInsideWebFrameViews];
1560 [[self window] makeFirstResponder:view];
1562 [self updateTextBackgroundColor];
1566 // This approach could be relaxed when dealing with 3228554
1567 - (BOOL)resignFirstResponder
1569 BOOL resign = [super resignFirstResponder];
1572 [self updateTextBackgroundColor];
1577 //------------------------------------------------------------------------------------
1578 // WebDocumentView protocol
1579 //------------------------------------------------------------------------------------
1580 - (void)setDataSource:(WebDataSource *)dataSource
1584 - (void)dataSourceUpdated:(WebDataSource *)dataSource
1588 // Does setNeedsDisplay:NO as a side effect. Useful for begin/endDocument.
1589 // pageWidth != 0 implies we will relayout to a new width
1590 - (void)_setPrinting:(BOOL)printing pageWidth:(float)pageWidth adjustViewSize:(BOOL)adjustViewSize
1592 WebFrame *frame = [self _frame];
1593 NSArray *subframes = [frame childFrames];
1594 unsigned n = [subframes count];
1596 for (i = 0; i != n; ++i) {
1597 WebFrame *subframe = [subframes objectAtIndex:i];
1598 WebFrameView *frameView = [subframe frameView];
1599 if ([[subframe dataSource] _isDocumentHTML]) {
1600 [(WebHTMLView *)[frameView documentView] _setPrinting:printing pageWidth:0 adjustViewSize:adjustViewSize];
1604 if (printing != _private->printing) {
1605 _private->printing = printing;
1606 [self setNeedsToApplyStyles:YES];
1607 [self setNeedsLayout:YES];
1608 [self layoutToPageWidth:pageWidth adjustingViewSize:adjustViewSize];
1609 [self setNeedsDisplay:NO];
1613 - (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit
1615 // This helps when we print as part of a larger print process.
1616 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1617 BOOL wasInPrintingMode = _private->printing;
1618 if (!wasInPrintingMode) {
1619 [self _setPrinting:YES pageWidth:0 adjustViewSize:NO];
1622 [[self _bridge] adjustPageHeightNew:newBottom top:oldTop bottom:oldBottom limit:bottomLimit];
1624 if (!wasInPrintingMode) {
1625 [self _setPrinting:NO pageWidth:0 adjustViewSize:NO];
1629 - (void)beginDocument
1631 // Must do this explicit display here, because otherwise the view might redisplay while the print
1632 // sheet was up, using printer fonts (and looking different).
1633 [self displayIfNeeded];
1634 [[self window] setAutodisplay:NO];
1636 // If we are a frameset just print with the layout we have onscreen, otherwise relayout
1637 // according to the paper size
1638 float pageWidth = 0.0;
1639 if (![[self _bridge] isFrameSet]) {
1640 NSPrintInfo *printInfo = [[NSPrintOperation currentOperation] printInfo];
1641 pageWidth = [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
1643 [self _setPrinting:YES pageWidth:pageWidth adjustViewSize:YES]; // will relayout
1645 [super beginDocument];
1646 // There is a theoretical chance that someone could do some drawing between here and endDocument,
1647 // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
1648 // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
1653 [super endDocument];
1654 // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO
1655 [self _setPrinting:NO pageWidth:0.0 adjustViewSize:YES];
1656 [[self window] setAutodisplay:YES];
1659 - (void)_updateTextSizeMultiplier
1661 [[self _bridge] setTextSizeMultiplier:[[self _webView] textSizeMultiplier]];
1664 - (void)keyDown:(NSEvent *)event
1666 if (![[self _bridge] interceptKeyEvent:event toView:self]) {
1667 [super keyDown:event];
1671 - (void)keyUp:(NSEvent *)event
1673 if (![[self _bridge] interceptKeyEvent:event toView:self]) {
1674 [super keyUp:event];
1678 - (id)accessibilityAttributeValue:(NSString*)attributeName
1680 if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
1681 id accTree = [[self _bridge] accessibilityTree];
1683 return [NSArray arrayWithObject: accTree];
1686 return [super accessibilityAttributeValue:attributeName];
1689 - (id)accessibilityHitTest:(NSPoint)point
1691 id accTree = [[self _bridge] accessibilityTree];
1693 NSPoint windowCoord = [[self window] convertScreenToBase: point];
1694 return [accTree accessibilityHitTest: [self convertPoint:windowCoord fromView:nil]];
1701 @implementation WebHTMLView (TextSizing)
1703 - (void)_web_textSizeMultiplierChanged
1705 [self _updateTextSizeMultiplier];
1710 @implementation NSArray (WebHTMLView)
1712 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object
1714 NSEnumerator *enumerator = [self objectEnumerator];
1715 WebNetscapePluginEmbeddedView *view;
1716 while ((view = [enumerator nextObject]) != nil) {
1717 if ([view isKindOfClass:[WebNetscapePluginEmbeddedView class]]) {
1718 [view performSelector:selector withObject:object];