2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #import "WebHTMLView.h"
32 #import "DOMNodeInternal.h"
33 #import "DOMRangeInternal.h"
34 #import "WebArchive.h"
35 #import "WebArchiver.h"
36 #import "WebBaseNetscapePluginViewInternal.h"
37 #import "WebClipView.h"
38 #import "WebDOMOperationsPrivate.h"
39 #import "WebDataSourceInternal.h"
40 #import "WebDefaultUIDelegate.h"
41 #import "WebDocumentInternal.h"
42 #import "WebDynamicScrollBarsView.h"
43 #import "WebEditingDelegate.h"
44 #import "WebElementDictionary.h"
45 #import "WebFrameInternal.h"
46 #import "WebFramePrivate.h"
47 #import "WebFrameViewInternal.h"
48 #import "WebHTMLRepresentationPrivate.h"
49 #import "WebHTMLViewInternal.h"
50 #import "WebKitLogging.h"
51 #import "WebKitNSStringExtras.h"
52 #import "WebKitPluginContainerView.h"
53 #import "WebKitVersionChecks.h"
54 #import "WebLocalizableStrings.h"
55 #import "WebNSAttributedStringExtras.h"
56 #import "WebNSEventExtras.h"
57 #import "WebNSFileManagerExtras.h"
58 #import "WebNSImageExtras.h"
59 #import "WebNSObjectExtras.h"
60 #import "WebNSPasteboardExtras.h"
61 #import "WebNSPrintOperationExtras.h"
62 #import "WebNSURLExtras.h"
63 #import "WebNSViewExtras.h"
64 #import "WebNetscapePluginEmbeddedView.h"
65 #import "WebPluginController.h"
66 #import "WebPreferences.h"
67 #import "WebPreferencesPrivate.h"
68 #import "WebResourcePrivate.h"
69 #import "WebStringTruncator.h"
70 #import "WebTypesInternal.h"
71 #import "WebUIDelegatePrivate.h"
72 #import "WebViewInternal.h"
73 #import <AppKit/NSAccessibility.h>
74 #import <ApplicationServices/ApplicationServices.h>
76 #import <WebCore/CachedImage.h>
77 #import <WebCore/CachedResourceClient.h>
78 #import <WebCore/ColorMac.h>
79 #import <WebCore/ContextMenu.h>
80 #import <WebCore/ContextMenuController.h>
81 #import <WebCore/Document.h>
82 #import <WebCore/Editor.h>
83 #import <WebCore/EditorDeleteAction.h>
84 #import <WebCore/Element.h>
85 #import <WebCore/EventHandler.h>
86 #import <WebCore/EventNames.h>
87 #import <WebCore/ExceptionHandlers.h>
88 #import <WebCore/DragController.h>
89 #import <WebCore/FloatRect.h>
90 #import <WebCore/FocusController.h>
91 #import <WebCore/Frame.h>
92 #import <WebCore/FrameLoader.h>
93 #import <WebCore/FrameView.h>
94 #import <WebCore/HitTestResult.h>
95 #import <WebCore/HTMLNames.h>
96 #import <WebCore/Image.h>
97 #import <WebCore/KeyboardEvent.h>
98 #import <WebCore/MIMETypeRegistry.h>
99 #import <WebCore/Page.h>
100 #import <WebCore/PlatformKeyboardEvent.h>
101 #import <WebCore/PlatformMouseEvent.h>
102 #import <WebCore/Range.h>
103 #import <WebCore/SelectionController.h>
104 #import <WebCore/SharedBuffer.h>
105 #import <WebCore/Text.h>
106 #import <WebCore/WebCoreObjCExtras.h>
107 #import <WebCore/WebCoreTextRenderer.h>
108 #import <WebKit/DOM.h>
109 #import <WebKit/DOMExtensions.h>
110 #import <WebKit/DOMPrivate.h>
111 #import <WebKitSystemInterface.h>
113 using namespace WebCore;
114 using namespace HTMLNames;
117 @interface NSWindow (BorderViewAccess)
118 - (NSView*)_web_borderView;
121 @implementation NSWindow (BorderViewAccess)
122 - (NSView*)_web_borderView
128 static IMP oldSetCursorIMP = NULL;
130 #ifdef BUILDING_ON_TIGER
131 static IMP oldResetCursorRectsIMP = NULL;
132 static BOOL canSetCursor = YES;
134 static void resetCursorRects(NSWindow* self, SEL cmd)
136 NSPoint point = [self mouseLocationOutsideOfEventStream];
137 NSView* view = [[self _web_borderView] hitTest:point];
138 if ([view isKindOfClass:[WebHTMLView class]]) {
139 WebHTMLView *htmlView = (WebHTMLView*)view;
140 NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
141 NSDictionary *dict = [htmlView elementAtPoint:point allowShadowContent:NO];
142 DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
143 if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
144 ![element isKindOfClass:[DOMHTMLEmbedElement class]])
147 oldResetCursorRectsIMP(self, cmd);
151 static void setCursor(NSCursor* self, SEL cmd)
154 oldSetCursorIMP(self, cmd);
157 static void setCursor(NSWindow* self, SEL cmd, NSPoint point)
159 NSView* view = [[self _web_borderView] hitTest:point];
160 if ([view isKindOfClass:[WebHTMLView class]]) {
161 WebHTMLView *htmlView = (WebHTMLView*)view;
162 NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
163 NSDictionary *dict = [htmlView elementAtPoint:point allowShadowContent:NO];
164 DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
165 if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
166 ![element isKindOfClass:[DOMHTMLEmbedElement class]])
169 oldSetCursorIMP(self, cmd, point);
175 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
177 extern NSString *NSMarkedClauseSegmentAttributeName;
178 extern NSString *NSTextInputReplacementRangeAttributeName;
182 @interface NSView (WebNSViewDetails)
183 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
184 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
185 - (NSRect)_dirtyRect;
186 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
187 - (void)_propagateDirtyRectsToOpaqueAncestors;
188 - (void)_windowChangedKeyState;
191 @interface NSApplication (WebNSApplicationDetails)
192 - (void)speakString:(NSString *)string;
195 @interface NSWindow (WebNSWindowDetails)
196 - (id)_newFirstResponderAfterResigning;
197 - (void)_setForceActiveControls:(BOOL)flag;
200 @interface NSAttributedString (WebNSAttributedStringDetails)
201 - (id)_initWithDOMRange:(DOMRange *)range;
202 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
205 @interface NSSpellChecker (WebNSSpellCheckerDetails)
206 - (void)learnWord:(NSString *)word;
209 // By imaging to a width a little wider than the available pixels,
210 // thin pages will be scaled down a little, matching the way they
211 // print in IE and Camino. This lets them use fewer sheets than they
212 // would otherwise, which is presumably why other browsers do this.
213 // Wide pages will be scaled down more than this.
214 #define PrintingMinimumShrinkFactor 1.25f
216 // This number determines how small we are willing to reduce the page content
217 // in order to accommodate the widest line. If the page would have to be
218 // reduced smaller to make the widest line fit, we just clip instead (this
219 // behavior matches MacIE and Mozilla, at least)
220 #define PrintingMaximumShrinkFactor 2.0f
222 // This number determines how short the last printed page of a multi-page print session
223 // can be before we try to shrink the scale in order to reduce the number of pages, and
224 // thus eliminate the orphan.
225 #define LastPrintedPageOrphanRatio 0.1f
227 // This number determines the amount the scale factor is adjusted to try to eliminate orphans.
228 // It has no direct mathematical relationship to LastPrintedPageOrphanRatio, due to variable
229 // numbers of pages, logic to avoid breaking elements, and CSS-supplied hard page breaks.
230 #define PrintingOrphanShrinkAdjustment 1.1f
232 #define AUTOSCROLL_INTERVAL 0.1f
234 #define DRAG_LABEL_BORDER_X 4.0f
235 //Keep border_y in synch with DragController::LinkDragBorderInset
236 #define DRAG_LABEL_BORDER_Y 2.0f
237 #define DRAG_LABEL_RADIUS 5.0f
238 #define DRAG_LABEL_BORDER_Y_OFFSET 2.0f
240 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120.0f
241 #define MAX_DRAG_LABEL_WIDTH 320.0f
243 #define DRAG_LINK_LABEL_FONT_SIZE 11.0f
244 #define DRAG_LINK_URL_FONT_SIZE 10.0f
246 // Any non-zero value will do, but using something recognizable might help us debug some day.
247 #define TRACKING_RECT_TAG 0xBADFACE
249 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
250 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
252 #define STANDARD_WEIGHT 5
253 #define MIN_BOLD_WEIGHT 9
254 #define STANDARD_BOLD_WEIGHT 10
257 #define WebDataProtocolScheme @"webkit-fake-url"
259 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
260 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
261 @interface WebCoreScrollView : NSScrollView
264 @implementation WebCoreScrollView
267 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
268 static BOOL forceNSViewHitTest;
270 // if YES, do the "top WebHTMLView" hit test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721])
271 static BOOL forceWebHTMLViewHitTest;
273 static WebHTMLView *lastHitView;
275 // We need this to be able to safely reference the CachedImage for the promised drag data
276 static CachedResourceClient* promisedDataClient()
278 static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
279 return staticCachedResourceClient;
282 @interface WebHTMLView (WebHTMLViewFileInternal)
283 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
284 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
285 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
286 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
287 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
288 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
289 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
290 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
291 - (float)_calculatePrintHeight;
292 - (DOMRange *)_selectedRange;
293 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
294 - (NSView *)_hitViewForEvent:(NSEvent *)event;
295 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
296 - (DOMRange *)_documentRange;
297 - (void)_setMouseDownEvent:(NSEvent *)event;
298 - (WebHTMLView *)_topHTMLView;
299 - (BOOL)_isTopHTMLView;
300 - (void)_web_setPrintingModeRecursive;
301 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
302 - (void)_web_clearPrintingModeRecursive;
305 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
306 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize;
309 @class NSInputContext;
310 @interface NSResponder (IMSecretsIKnowAbout)
311 - (NSInputContext *)inputContext;
314 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
315 - (void)_updateSelectionForInputManager;
318 @interface WebHTMLView (WebEditingStyleSupport)
319 - (DOMCSSStyleDeclaration *)_emptyStyle;
320 - (NSString *)_colorAsString:(NSColor *)color;
323 @interface NSView (WebHTMLViewFileInternal)
324 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
327 @interface NSMutableDictionary (WebHTMLViewFileInternal)
328 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
331 // Handles the complete: text command
332 @interface WebTextCompleteController : NSObject {
335 NSWindow *_popupWindow;
336 NSTableView *_tableView;
337 NSArray *_completions;
338 NSString *_originalString;
341 - (id)initWithHTMLView:(WebHTMLView *)view;
342 - (void)doCompletion;
343 - (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft;
344 - (BOOL)popupWindowIsOpen;
345 - (BOOL)filterKeyDown:(NSEvent *)event;
346 - (void)_reflectSelection;
349 struct WebHTMLViewInterpretKeyEventsParameters {
350 KeyboardEvent* event;
351 BOOL eventWasHandled;
352 BOOL shouldSaveCommand;
353 // The Input Method may consume an event and not tell us, in
354 // which case we should not bubble the event up the DOM
358 @interface WebHTMLViewPrivate : NSObject {
362 BOOL needsToApplyStyles;
363 BOOL ignoringMouseDraggedEvents;
365 BOOL avoidingPrintOrphan;
368 BOOL subviewsSetAside;
370 NSEvent *mouseDownEvent; // Kept after handling the event.
371 BOOL handlingMouseDownEvent;
372 NSEvent *keyDownEvent; // Kept after handling the event.
374 NSSize lastLayoutSize;
376 NSPoint lastScrollPosition;
378 WebPluginController *pluginController;
381 NSToolTipTag lastToolTipTag;
382 id trackingRectOwner;
383 void *trackingRectUserData;
385 NSTimer *autoscrollTimer;
386 NSEvent *autoscrollTriggerEvent;
390 NSMutableDictionary* highlighters;
392 BOOL resigningFirstResponder;
393 BOOL nextResponderDisabledOnce;
395 WebTextCompleteController *compController;
397 BOOL transparentBackground;
399 WebHTMLViewInterpretKeyEventsParameters *interpretKeyEventsParameters;
402 WebDataSource *dataSource;
403 WebCore::CachedImage *promisedDragTIFFDataSource;
405 CFRunLoopTimerRef updateFocusedAndActiveStateTimer;
406 CFRunLoopTimerRef updateMouseoverTimer;
408 SEL selectorForDoCommandBySelector;
411 BOOL enumeratingSubviews;
417 static NSCellStateValue kit(TriState state)
427 ASSERT_NOT_REACHED();
431 @implementation WebHTMLViewPrivate
435 #ifndef BUILDING_ON_TIGER
436 WebCoreObjCFinalizeOnMainThread(self);
439 if (!oldSetCursorIMP) {
440 #ifdef BUILDING_ON_TIGER
441 Method setCursorMethod = class_getInstanceMethod([NSCursor class], @selector(set));
443 Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
445 ASSERT(setCursorMethod);
447 oldSetCursorIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
448 ASSERT(oldSetCursorIMP);
451 #ifdef BUILDING_ON_TIGER
452 if (!oldResetCursorRectsIMP) {
453 Method resetCursorRectsMethod = class_getInstanceMethod([NSWindow class], @selector(resetCursorRects));
454 ASSERT(resetCursorRectsMethod);
455 oldResetCursorRectsIMP = method_setImplementation(resetCursorRectsMethod, (IMP)resetCursorRects);
456 ASSERT(oldResetCursorRectsIMP);
464 ASSERT(!autoscrollTimer);
465 ASSERT(!autoscrollTriggerEvent);
466 ASSERT(!updateFocusedAndActiveStateTimer);
467 ASSERT(!updateMouseoverTimer);
469 [mouseDownEvent release];
470 [keyDownEvent release];
471 [pluginController release];
473 [compController release];
474 [dataSource release];
475 [highlighters release];
476 if (promisedDragTIFFDataSource)
477 promisedDragTIFFDataSource->deref(promisedDataClient());
484 ASSERT_MAIN_THREAD();
486 if (promisedDragTIFFDataSource)
487 promisedDragTIFFDataSource->deref(promisedDataClient());
494 [mouseDownEvent release];
495 [keyDownEvent release];
496 [pluginController release];
498 [compController release];
499 [dataSource release];
500 [highlighters release];
501 if (promisedDragTIFFDataSource)
502 promisedDragTIFFDataSource->deref(promisedDataClient());
504 mouseDownEvent = nil;
506 pluginController = nil;
508 compController = nil;
511 promisedDragTIFFDataSource = 0;
516 @implementation WebHTMLView (WebHTMLViewFileInternal)
518 - (DOMRange *)_documentRange
520 return [[[self _frame] DOMDocument] _documentRange];
523 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
525 NSEnumerator *enumerator = [paths objectEnumerator];
528 while ((path = [enumerator nextObject]) != nil) {
529 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
530 if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
537 - (WebDataSource *)_dataSource
539 return _private->dataSource;
542 - (WebView *)_webView
544 return [_private->dataSource _webView];
547 - (WebFrameView *)_frameView
549 return [[_private->dataSource webFrame] frameView];
552 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
554 DOMDocumentFragment *fragment;
555 NSEnumerator *enumerator = [paths objectEnumerator];
556 NSMutableArray *domNodes = [[NSMutableArray alloc] init];
559 while ((path = [enumerator nextObject]) != nil) {
560 // Non-image file types; _web_userVisibleString is appropriate here because this will
561 // be pasted as visible text.
562 NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
563 [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
566 fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes];
570 return [fragment firstChild] != nil ? fragment : nil;
573 + (NSArray *)_excludedElementsForAttributedStringConversion
575 static NSArray *elements = nil;
576 if (elements == nil) {
577 elements = [[NSArray alloc] initWithObjects:
578 // Omit style since we want style to be inline so the fragment can be easily inserted.
580 // Omit xml so the result is not XHTML.
582 // Omit tags that will get stripped when converted to a fragment anyway.
583 @"doctype", @"html", @"head", @"body",
584 // Omit deprecated tags.
585 @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
586 // Omit object so no file attachments are part of the fragment.
593 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
595 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
596 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
598 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
599 CFRelease(UUIDString);
604 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
605 inContext:(DOMRange *)context
606 allowPlainText:(BOOL)allowPlainText
608 NSArray *types = [pasteboard types];
609 DOMDocumentFragment *fragment = nil;
611 if ([types containsObject:WebArchivePboardType] &&
612 (fragment = [self _documentFragmentFromPasteboard:pasteboard
613 forType:WebArchivePboardType
618 if ([types containsObject:NSFilenamesPboardType] &&
619 (fragment = [self _documentFragmentFromPasteboard:pasteboard
620 forType:NSFilenamesPboardType
625 if ([types containsObject:NSHTMLPboardType] &&
626 (fragment = [self _documentFragmentFromPasteboard:pasteboard
627 forType:NSHTMLPboardType
632 if ([types containsObject:NSRTFPboardType] &&
633 (fragment = [self _documentFragmentFromPasteboard:pasteboard
634 forType:NSRTFPboardType
639 if ([types containsObject:NSRTFDPboardType] &&
640 (fragment = [self _documentFragmentFromPasteboard:pasteboard
641 forType:NSRTFDPboardType
646 if ([types containsObject:NSTIFFPboardType] &&
647 (fragment = [self _documentFragmentFromPasteboard:pasteboard
648 forType:NSTIFFPboardType
653 if ([types containsObject:NSPICTPboardType] &&
654 (fragment = [self _documentFragmentFromPasteboard:pasteboard
655 forType:NSPICTPboardType
660 if ([types containsObject:NSURLPboardType] &&
661 (fragment = [self _documentFragmentFromPasteboard:pasteboard
662 forType:NSURLPboardType
667 if (allowPlainText && [types containsObject:NSStringPboardType] &&
668 (fragment = [self _documentFragmentFromPasteboard:pasteboard
669 forType:NSStringPboardType
678 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
680 NSArray *types = [pasteboard types];
682 if ([types containsObject:NSStringPboardType])
683 return [pasteboard stringForType:NSStringPboardType];
685 NSAttributedString *attributedString = nil;
688 if ([types containsObject:NSRTFDPboardType])
689 attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
690 if (attributedString == nil && [types containsObject:NSRTFPboardType])
691 attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
692 if (attributedString != nil) {
693 string = [[attributedString string] copy];
694 [attributedString release];
695 return [string autorelease];
698 if ([types containsObject:NSFilenamesPboardType]) {
699 string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
706 if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
707 string = [URL _web_userVisibleString];
708 if ([string length] > 0)
715 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
717 DOMRange *range = [self _selectedRange];
718 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard
719 inContext:range allowPlainText:allowPlainText];
720 WebFrame *frame = [self _frame];
721 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[self _selectedRange] givenAction:WebViewInsertActionPasted]) {
722 [frame _replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:NO];
726 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
728 NSString *text = [self _plainTextFromPasteboard:pasteboard];
729 if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
730 [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
733 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
735 WebView *webView = [self _webView];
736 DOMNode *child = [fragment firstChild];
737 if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
738 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
739 return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
742 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
744 WebView *webView = [self _webView];
745 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
748 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
750 return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
753 // Calculate the vertical size of the view that fits on a single page
754 - (float)_calculatePrintHeight
756 // Obtain the print info object for the current operation
757 NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
759 // Calculate the page height in points
760 NSSize paperSize = [pi paperSize];
761 return paperSize.height - [pi topMargin] - [pi bottomMargin];
764 - (DOMRange *)_selectedRange
766 Frame* coreFrame = core([self _frame]);
767 return coreFrame ? kit(coreFrame->selectionController()->toRange().get()) : nil;
770 - (BOOL)_shouldDeleteRange:(DOMRange *)range
772 Frame* coreFrame = core([self _frame]);
773 return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
776 - (NSView *)_hitViewForEvent:(NSEvent *)event
778 // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.
779 // Callers of this method, however, want to query the deepest view instead.
780 forceNSViewHitTest = YES;
781 NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
782 forceNSViewHitTest = NO;
786 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
788 // Put HTML on the pasteboard.
789 if ([types containsObject:WebArchivePboardType]) {
790 WebArchive *archive = [WebArchiver archiveSelectionInFrame:[self _frame]];
791 [pasteboard setData:[archive data] forType:WebArchivePboardType];
794 // Put the attributed string on the pasteboard (RTF/RTFD format).
795 if ([types containsObject:NSRTFDPboardType]) {
796 if (attributedString == nil) {
797 attributedString = [self selectedAttributedString];
799 NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
800 [pasteboard setData:RTFDData forType:NSRTFDPboardType];
802 if ([types containsObject:NSRTFPboardType]) {
803 if (attributedString == nil) {
804 attributedString = [self selectedAttributedString];
806 if ([attributedString containsAttachments]) {
807 attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
809 NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
810 [pasteboard setData:RTFData forType:NSRTFPboardType];
813 // Put plain string on the pasteboard.
814 if ([types containsObject:NSStringPboardType]) {
815 // Map to a plain old space because this is better for source code, other browsers do it,
816 // and because HTML forces you to do this any time you want two spaces in a row.
817 NSMutableString *s = [[self selectedString] mutableCopy];
818 const unichar NonBreakingSpaceCharacter = 0xA0;
819 NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
820 [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
821 [pasteboard setString:s forType:NSStringPboardType];
825 if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
826 [pasteboard setData:nil forType:WebSmartPastePboardType];
830 - (void)_setMouseDownEvent:(NSEvent *)event
832 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
834 if (event == _private->mouseDownEvent)
838 [_private->mouseDownEvent release];
839 _private->mouseDownEvent = event;
842 - (void)_cancelUpdateFocusedAndActiveStateTimer
844 if (_private->updateFocusedAndActiveStateTimer) {
845 CFRunLoopTimerInvalidate(_private->updateFocusedAndActiveStateTimer);
846 CFRelease(_private->updateFocusedAndActiveStateTimer);
847 _private->updateFocusedAndActiveStateTimer = NULL;
851 - (void)_cancelUpdateMouseoverTimer
853 if (_private->updateMouseoverTimer) {
854 CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
855 CFRelease(_private->updateMouseoverTimer);
856 _private->updateMouseoverTimer = NULL;
860 - (WebHTMLView *)_topHTMLView
862 // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
863 WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
865 ASSERT([view isKindOfClass:[WebHTMLView class]]);
869 - (BOOL)_isTopHTMLView
871 // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
872 return self == [self _topHTMLView];
875 - (void)_web_setPrintingModeRecursive
877 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
880 _private->enumeratingSubviews = YES;
883 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
885 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
887 unsigned count = [descendantWebHTMLViews count];
888 for (unsigned i = 0; i < count; ++i)
889 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
891 [descendantWebHTMLViews release];
894 _private->enumeratingSubviews = NO;
898 - (void)_web_clearPrintingModeRecursive
900 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
903 _private->enumeratingSubviews = YES;
906 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
908 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
910 unsigned count = [descendantWebHTMLViews count];
911 for (unsigned i = 0; i < count; ++i)
912 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
914 [descendantWebHTMLViews release];
917 _private->enumeratingSubviews = NO;
921 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize
923 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
926 _private->enumeratingSubviews = YES;
929 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
931 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
933 unsigned count = [descendantWebHTMLViews count];
934 for (unsigned i = 0; i < count; ++i)
935 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
937 [descendantWebHTMLViews release];
940 _private->enumeratingSubviews = NO;
946 @implementation WebHTMLView (WebPrivate)
948 + (NSArray *)supportedMIMETypes
950 return [WebHTMLRepresentation supportedMIMETypes];
953 + (NSArray *)supportedImageMIMETypes
955 return [WebHTMLRepresentation supportedImageMIMETypes];
958 + (NSArray *)supportedNonImageMIMETypes
960 return [WebHTMLRepresentation supportedNonImageMIMETypes];
963 + (NSArray *)unsupportedTextMIMETypes
965 return [NSArray arrayWithObjects:
966 @"text/calendar", // iCal
970 @"text/vcard", // vCard
973 @"text/ldif", // Netscape Address Book
974 @"text/qif", // Quicken
976 @"text/x-csv", // CSV (for Address Book and Microsoft Outlook)
977 @"text/x-vcf", // vCard type used in Sun affinity app
978 @"text/rtf", // Rich Text Format
982 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
984 // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged
985 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
986 location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
987 modifierFlags:[flagsChangedEvent modifierFlags]
988 timestamp:[flagsChangedEvent timestamp]
989 windowNumber:[flagsChangedEvent windowNumber]
990 context:[flagsChangedEvent context]
991 eventNumber:0 clickCount:0 pressure:0];
993 // Pretend it's a mouse move.
994 [[NSNotificationCenter defaultCenter]
995 postNotificationName:WKMouseMovedNotification() object:self
996 userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
999 - (void)_updateMouseoverWithFakeEvent
1001 [self _cancelUpdateMouseoverTimer];
1003 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1004 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1005 modifierFlags:[[NSApp currentEvent] modifierFlags]
1006 timestamp:[NSDate timeIntervalSinceReferenceDate]
1007 windowNumber:[[self window] windowNumber]
1008 context:[[NSApp currentEvent] context]
1009 eventNumber:0 clickCount:0 pressure:0];
1011 [self _updateMouseoverWithEvent:fakeEvent];
1014 static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
1016 WebHTMLView *view = (WebHTMLView *)info;
1018 [view _updateMouseoverWithFakeEvent];
1021 - (void)_frameOrBoundsChanged
1023 if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
1024 [self setNeedsLayout:YES];
1025 [self setNeedsDisplay:YES];
1026 [_private->compController endRevertingChange:NO moveLeft:NO];
1029 NSPoint origin = [[self superview] bounds].origin;
1030 if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
1031 [[self _frame] _sendScrollEvent];
1032 [_private->compController endRevertingChange:NO moveLeft:NO];
1034 WebView *webView = [self _webView];
1035 [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
1037 _private->lastScrollPosition = origin;
1039 if ([self window] && !_private->closed && !_private->updateMouseoverTimer) {
1040 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
1042 // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading
1043 // and scrolling rapidly back to back.
1044 _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0,
1045 _updateMouseoverTimerCallback, &context);
1046 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode);
1050 - (void)_setAsideSubviews
1052 ASSERT(!_private->subviewsSetAside);
1053 ASSERT(_private->savedSubviews == nil);
1054 _private->savedSubviews = _subviews;
1056 _private->subviewsSetAside = YES;
1059 - (void)_restoreSubviews
1061 ASSERT(_private->subviewsSetAside);
1062 ASSERT(_subviews == nil);
1063 _subviews = _private->savedSubviews;
1064 _private->savedSubviews = nil;
1065 _private->subviewsSetAside = NO;
1070 - (void)didAddSubview:(NSView *)subview
1072 if (_private->enumeratingSubviews)
1073 LOG(View, "A view of class %s was added during subview enumeration for layout or printing mode change. This view might paint without first receiving layout.", object_getClassName([subview class]));
1076 - (void)willRemoveSubview:(NSView *)subview
1078 if (_private->enumeratingSubviews)
1079 LOG(View, "A view of class %s was removed during subview enumeration for layout or printing mode change. We will still do layout or the printing mode change even though this view is no longer in the view hierarchy.", object_getClassName([subview class]));
1084 #ifdef BUILDING_ON_TIGER
1086 // This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
1087 // That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty
1088 // before doing it. As a compromise, when we're opaque we do the layout only when actually asked to
1089 // draw, but when we're transparent we do the layout at this stage so views behind us know that they
1090 // need to be redrawn (in case the layout causes some things to get dirtied).
1091 - (void)_propagateDirtyRectsToOpaqueAncestors
1093 if (![[self _webView] drawsBackground])
1094 [self _web_layoutIfNeededRecursive];
1095 [super _propagateDirtyRectsToOpaqueAncestors];
1100 - (void)viewWillDraw
1102 // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1103 // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1104 // once the FIXME in _isTopHTMLView is fixed.
1105 if (_private->dataSource && [self _isTopHTMLView])
1106 [self _web_layoutIfNeededRecursive];
1107 [super viewWillDraw];
1112 // Don't let AppKit even draw subviews. We take care of that.
1113 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1115 // This helps when we print as part of a larger print process.
1116 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1117 BOOL wasInPrintingMode = _private->printing;
1118 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1119 if (wasInPrintingMode != isPrinting) {
1121 [self _web_setPrintingModeRecursive];
1123 [self _web_clearPrintingModeRecursive];
1126 #ifdef BUILDING_ON_TIGER
1128 // Because Tiger does not have viewWillDraw we need to do layout here.
1129 [self _web_layoutIfNeededRecursive];
1130 [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
1134 [self _setAsideSubviews];
1135 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1136 [self _restoreSubviews];
1138 if (wasInPrintingMode != isPrinting) {
1139 if (wasInPrintingMode)
1140 [self _web_setPrintingModeRecursive];
1142 [self _web_clearPrintingModeRecursive];
1146 // Don't let AppKit even draw subviews. We take care of that.
1147 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1149 BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1151 BOOL wasInPrintingMode = _private->printing;
1152 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1154 if (needToSetAsideSubviews) {
1155 // This helps when we print as part of a larger print process.
1156 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1157 if (wasInPrintingMode != isPrinting) {
1159 [self _web_setPrintingModeRecursive];
1161 [self _web_clearPrintingModeRecursive];
1164 #ifdef BUILDING_ON_TIGER
1166 // Because Tiger does not have viewWillDraw we need to do layout here.
1167 NSRect boundsBeforeLayout = [self bounds];
1168 if (!NSIsEmptyRect(visRect))
1169 [self _web_layoutIfNeededRecursive];
1171 // If layout changes the view's bounds, then we need to recompute the visRect.
1172 // That's because the visRect passed to us was based on the bounds at the time
1173 // we were called. This method is only displayed to draw "all", so it's safe
1174 // to just call visibleRect to compute the entire rectangle.
1175 if (!NSEqualRects(boundsBeforeLayout, [self bounds]))
1176 visRect = [self visibleRect];
1180 [self _setAsideSubviews];
1183 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1185 if (needToSetAsideSubviews) {
1186 if (wasInPrintingMode != isPrinting) {
1187 if (wasInPrintingMode)
1188 [self _web_setPrintingModeRecursive];
1190 [self _web_clearPrintingModeRecursive];
1193 [self _restoreSubviews];
1197 - (BOOL)_insideAnotherHTMLView
1199 return self != [self _topHTMLView];
1202 - (NSView *)hitTest:(NSPoint)point
1204 // WebHTMLView objects handle all events for objects inside them.
1205 // To get those events, we prevent hit testing from AppKit.
1207 // But there are three exceptions to this:
1208 // 1) For right mouse clicks and control clicks we don't yet have an implementation
1209 // that works for nested views, so we let the hit testing go through the
1210 // standard NSView code path (needs to be fixed, see bug 4361618).
1211 // 2) Java depends on doing a hit test inside it's mouse moved handling,
1212 // so we let the hit testing go through the standard NSView code path
1213 // when the current event is a mouse move (except when we are calling
1214 // from _updateMouseoverWithEvent, so we have to use a global,
1215 // forceWebHTMLViewHitTest, for that)
1216 // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1217 // both need to figure out which view to check with inside the WebHTMLView.
1218 // They use a global to change the behavior of hitTest: so they can get the
1219 // right view. The global is forceNSViewHitTest and the method they use to
1220 // do the hit testing is _hitViewForEvent:. (But this does not work correctly
1221 // when there is HTML overlapping the view, see bug 4361626)
1222 // 4) NSAccessibilityHitTest relies on this for checking the cursor position.
1223 // Our check for that is whether the event is NSFlagsChanged. This works
1224 // for VoiceOver's cntl-opt-f5 command (move focus to item under cursor)
1225 // and Dictionary's cmd-cntl-D (open dictionary popup for item under cursor).
1226 // This is of course a hack.
1228 BOOL captureHitsOnSubviews;
1229 if (forceNSViewHitTest)
1230 captureHitsOnSubviews = NO;
1231 else if (forceWebHTMLViewHitTest)
1232 captureHitsOnSubviews = YES;
1234 NSEvent *event = [[self window] currentEvent];
1235 captureHitsOnSubviews = !([event type] == NSMouseMoved
1236 || [event type] == NSRightMouseDown
1237 || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1238 || [event type] == NSFlagsChanged);
1241 if (!captureHitsOnSubviews)
1242 return [super hitTest:point];
1243 if ([[self superview] mouse:point inRect:[self frame]])
1248 - (void)_clearLastHitViewIfSelf
1250 if (lastHitView == self)
1254 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1256 ASSERT(_private->trackingRectOwner == nil);
1257 _private->trackingRectOwner = owner;
1258 _private->trackingRectUserData = data;
1259 return TRACKING_RECT_TAG;
1262 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1264 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1265 ASSERT(_private->trackingRectOwner == nil);
1266 _private->trackingRectOwner = owner;
1267 _private->trackingRectUserData = data;
1268 return TRACKING_RECT_TAG;
1271 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1274 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1275 ASSERT(_private->trackingRectOwner == nil);
1276 _private->trackingRectOwner = owner;
1277 _private->trackingRectUserData = userDataList[0];
1278 trackingNums[0] = TRACKING_RECT_TAG;
1281 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1286 if (_private && (tag == TRACKING_RECT_TAG)) {
1287 _private->trackingRectOwner = nil;
1291 if (_private && (tag == _private->lastToolTipTag)) {
1292 [super removeTrackingRect:tag];
1293 _private->lastToolTipTag = 0;
1297 // If any other tracking rect is being removed, we don't know how it was created
1298 // and it's possible there's a leak involved (see 3500217)
1299 ASSERT_NOT_REACHED();
1302 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1305 for (i = 0; i < count; ++i) {
1309 ASSERT(tag == TRACKING_RECT_TAG);
1310 if (_private != nil) {
1311 _private->trackingRectOwner = nil;
1316 - (void)_sendToolTipMouseExited
1318 // Nothing matters except window, trackingNumber, and userData.
1319 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1320 location:NSMakePoint(0, 0)
1323 windowNumber:[[self window] windowNumber]
1326 trackingNumber:TRACKING_RECT_TAG
1327 userData:_private->trackingRectUserData];
1328 [_private->trackingRectOwner mouseExited:fakeEvent];
1331 - (void)_sendToolTipMouseEntered
1333 // Nothing matters except window, trackingNumber, and userData.
1334 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1335 location:NSMakePoint(0, 0)
1338 windowNumber:[[self window] windowNumber]
1341 trackingNumber:TRACKING_RECT_TAG
1342 userData:_private->trackingRectUserData];
1343 [_private->trackingRectOwner mouseEntered:fakeEvent];
1346 - (void)_setToolTip:(NSString *)string
1348 NSString *toolTip = [string length] == 0 ? nil : string;
1349 NSString *oldToolTip = _private->toolTip;
1350 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1354 [self _sendToolTipMouseExited];
1355 [oldToolTip release];
1357 _private->toolTip = [toolTip copy];
1359 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1360 [self removeAllToolTips];
1361 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1362 _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1363 [self _sendToolTipMouseEntered];
1367 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1369 return [[_private->toolTip copy] autorelease];
1372 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1374 if (_private->closed)
1377 NSView *contentView = [[event window] contentView];
1378 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1380 forceWebHTMLViewHitTest = YES;
1381 NSView *hitView = [contentView hitTest:locationForHitTest];
1382 forceWebHTMLViewHitTest = NO;
1384 WebHTMLView *view = nil;
1385 if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
1386 view = (WebHTMLView *)hitView;
1391 if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1392 // If we are moving out of a view (or frame), let's pretend the mouse moved
1393 // all the way out of that view. But we have to account for scrolling, because
1394 // khtml doesn't understand our clipping.
1395 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1396 float yScroll = visibleRect.origin.y;
1397 float xScroll = visibleRect.origin.x;
1399 event = [NSEvent mouseEventWithType:NSMouseMoved
1400 location:NSMakePoint(-1 - xScroll, -1 - yScroll )
1401 modifierFlags:[[NSApp currentEvent] modifierFlags]
1402 timestamp:[NSDate timeIntervalSinceReferenceDate]
1403 windowNumber:[[view window] windowNumber]
1404 context:[[NSApp currentEvent] context]
1405 eventNumber:0 clickCount:0 pressure:0];
1406 if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1407 lastHitCoreFrame->eventHandler()->mouseMoved(event);
1413 if (Frame* coreFrame = core([view _frame]))
1414 coreFrame->eventHandler()->mouseMoved(event);
1420 // keep in sync with WebPasteboardHelper::insertablePasteboardTypes
1421 + (NSArray *)_insertablePasteboardTypes
1423 static NSArray *types = nil;
1425 types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType,
1426 NSFilenamesPboardType, NSTIFFPboardType, NSPICTPboardType, NSURLPboardType,
1427 NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, nil];
1433 + (NSArray *)_selectionPasteboardTypes
1435 // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1436 return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1439 - (NSImage *)_dragImageForURL:(NSString*)urlString withLabel:(NSString*)label
1441 BOOL drawURLString = YES;
1442 BOOL clipURLString = NO, clipLabelString = NO;
1449 NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
1450 toHaveTrait:NSBoldFontMask];
1451 NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
1453 labelSize.width = [label _web_widthWithFont: labelFont];
1454 labelSize.height = [labelFont ascender] - [labelFont descender];
1455 if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
1456 labelSize.width = MAX_DRAG_LABEL_WIDTH;
1457 clipLabelString = YES;
1460 NSSize imageSize, urlStringSize;
1461 imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2.0f;
1462 imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2.0f;
1463 if (drawURLString) {
1464 urlStringSize.width = [urlString _web_widthWithFont: urlFont];
1465 urlStringSize.height = [urlFont ascender] - [urlFont descender];
1466 imageSize.height += urlStringSize.height;
1467 if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
1468 imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2.0f, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
1469 clipURLString = YES;
1471 imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2.0f, urlStringSize.width + DRAG_LABEL_BORDER_X * 2.0f);
1474 NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
1475 [dragImage lockFocus];
1477 [[NSColor colorWithCalibratedRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
1479 // Drag a rectangle with rounded corners/
1480 NSBezierPath *path = [NSBezierPath bezierPath];
1481 [path appendBezierPathWithOvalInRect: NSMakeRect(0.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1482 [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1483 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1484 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1486 [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0.0f, imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height)];
1487 [path appendBezierPathWithRect: NSMakeRect(0.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1488 [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS - 20.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 20.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1491 NSColor *topColor = [NSColor colorWithCalibratedWhite:0.0f alpha:0.75f];
1492 NSColor *bottomColor = [NSColor colorWithCalibratedWhite:1.0f alpha:0.5f];
1493 if (drawURLString) {
1495 urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:urlFont];
1497 [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender])
1498 withTopColor:topColor bottomColor:bottomColor font:urlFont];
1501 if (clipLabelString)
1502 label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:labelFont];
1503 [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
1504 withTopColor:topColor bottomColor:bottomColor font:labelFont];
1506 [dragImage unlockFocus];
1511 - (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
1513 NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
1515 NSString *label = [element objectForKey: WebElementLinkLabelKey];
1516 NSString *urlString = [linkURL _web_userVisibleString];
1517 return [self _dragImageForURL:urlString withLabel:label];
1520 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1522 [self setPromisedDragTIFFDataSource:0];
1525 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1527 if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1528 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1529 [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1531 } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1532 if (Image* image = [self promisedDragTIFFDataSource]->image())
1533 [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1534 [self setPromisedDragTIFFDataSource:0];
1538 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
1540 [self autoscroll:event];
1541 [self _startAutoscrollTimer:event];
1544 - (WebPluginController *)_pluginController
1546 return _private->pluginController;
1549 - (void)_layoutForPrinting
1551 // Set printing mode temporarily so we can adjust the size of the view. This will allow
1552 // AppKit's pagination code to use the correct height for the page content. Leaving printing
1553 // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1554 // turn it off again after adjusting the size.
1555 [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1556 [self _web_clearPrintingModeRecursive];
1559 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1561 if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
1563 *beforeString = nil;
1569 [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
1572 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
1574 return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
1577 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1579 if (_private->autoscrollTimer == nil) {
1580 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1581 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1582 _private->autoscrollTriggerEvent = [triggerEvent retain];
1586 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1587 // We can't remove this yet because it's still in use by Mail.
1588 - (NSRect)_selectionRect
1590 return [self selectionRect];
1593 - (void)_stopAutoscrollTimer
1595 NSTimer *timer = _private->autoscrollTimer;
1596 _private->autoscrollTimer = nil;
1597 [_private->autoscrollTriggerEvent release];
1598 _private->autoscrollTriggerEvent = nil;
1605 // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1606 // a mouse up event.
1607 BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);
1609 [self _stopAutoscrollTimer];
1613 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1614 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1615 modifierFlags:[[NSApp currentEvent] modifierFlags]
1616 timestamp:[NSDate timeIntervalSinceReferenceDate]
1617 windowNumber:[[self window] windowNumber]
1618 context:[[NSApp currentEvent] context]
1619 eventNumber:0 clickCount:0 pressure:0];
1620 [self mouseDragged:fakeEvent];
1625 Frame* coreFrame = core([self _frame]);
1626 return coreFrame && coreFrame->editor()->canEdit();
1629 - (BOOL)_canEditRichly
1631 Frame* coreFrame = core([self _frame]);
1632 return coreFrame && coreFrame->editor()->canEditRichly();
1635 - (BOOL)_canAlterCurrentSelection
1637 return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1640 - (BOOL)_hasSelection
1642 Frame* coreFrame = core([self _frame]);
1643 return coreFrame && coreFrame->selectionController()->isRange();
1646 - (BOOL)_hasSelectionOrInsertionPoint
1648 Frame* coreFrame = core([self _frame]);
1649 return coreFrame && coreFrame->selectionController()->isCaretOrRange();
1652 - (BOOL)_hasInsertionPoint
1654 Frame* coreFrame = core([self _frame]);
1655 return coreFrame && coreFrame->selectionController()->isCaret();
1660 Frame* coreFrame = core([self _frame]);
1661 return coreFrame && coreFrame->selectionController()->isContentEditable();
1664 - (BOOL)_transparentBackground
1666 return _private->transparentBackground;
1669 - (void)_setTransparentBackground:(BOOL)f
1671 _private->transparentBackground = f;
1674 - (NSImage *)_selectionDraggingImage
1676 if ([self _hasSelection]) {
1677 NSImage *dragImage = core([self _frame])->selectionImage();
1678 [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1684 - (NSRect)_selectionDraggingRect
1686 // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1687 return [self selectionRect];
1690 - (DOMNode *)_insertOrderedList
1692 Frame* coreFrame = core([self _frame]);
1693 return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
1696 - (DOMNode *)_insertUnorderedList
1698 Frame* coreFrame = core([self _frame]);
1699 return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
1702 - (BOOL)_canIncreaseSelectionListLevel
1704 Frame* coreFrame = core([self _frame]);
1705 return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
1708 - (BOOL)_canDecreaseSelectionListLevel
1710 Frame* coreFrame = core([self _frame]);
1711 return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
1714 - (DOMNode *)_increaseSelectionListLevel
1716 Frame* coreFrame = core([self _frame]);
1717 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
1720 - (DOMNode *)_increaseSelectionListLevelOrdered
1722 Frame* coreFrame = core([self _frame]);
1723 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
1726 - (DOMNode *)_increaseSelectionListLevelUnordered
1728 Frame* coreFrame = core([self _frame]);
1729 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
1732 - (void)_decreaseSelectionListLevel
1734 Frame* coreFrame = core([self _frame]);
1736 coreFrame->editor()->decreaseSelectionListLevel();
1739 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1741 if (!_private->highlighters)
1742 _private->highlighters = [[NSMutableDictionary alloc] init];
1743 [_private->highlighters setObject:highlighter forKey:type];
1746 - (void)_removeHighlighterOfType:(NSString*)type
1748 [_private->highlighters removeObjectForKey:type];
1751 - (void)_updateFocusedAndActiveState
1753 [self _cancelUpdateFocusedAndActiveStateTimer];
1755 // This method does the job of updating the view based on the view's firstResponder-ness and
1756 // the window key-ness of the window containing this view. This involves four kinds of
1757 // drawing updates right now.
1759 // The four display attributes are as follows:
1761 // 1. The background color used to draw behind selected content (active | inactive color)
1762 // 2. Caret blinking (blinks | does not blink)
1763 // 3. The drawing of a focus ring around links in web pages.
1765 // Also, this is responsible for letting the bridge know if the window has gained or lost focus
1766 // so we can send focus and blur events.
1768 Frame* frame = core([self _frame]);
1772 Page* page = frame->page();
1776 NSWindow *window = [self window];
1777 BOOL windowIsKey = [window isKeyWindow];
1778 BOOL windowOrSheetIsKey = windowIsKey || [[window attachedSheet] isKeyWindow];
1780 // FIXME: this can move to WebView since active state is Page level, not Frame level.
1781 NSResponder *firstResponder = [window firstResponder];
1782 if (firstResponder == self || firstResponder == [self _frameView])
1783 page->focusController()->setActive(!_private->resigningFirstResponder && windowIsKey);
1785 Frame* focusedFrame = page->focusController()->focusedOrMainFrame();
1786 frame->selectionController()->setFocused(frame == focusedFrame && windowOrSheetIsKey);
1789 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1791 ASSERT([self _hasSelection]);
1792 NSArray *types = [self pasteboardTypesForSelection];
1794 // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1795 NSAttributedString *attributedString = [self selectedAttributedString];
1796 NSMutableArray *mutableTypes = nil;
1797 if (![attributedString containsAttachments]) {
1798 mutableTypes = [types mutableCopy];
1799 [mutableTypes removeObject:NSRTFDPboardType];
1800 types = mutableTypes;
1803 [pasteboard declareTypes:types owner:[self _topHTMLView]];
1804 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1805 [mutableTypes release];
1810 // Check for a nil _private here incase we were created with initWithCoder. In that case, the WebView is just throwing
1811 // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
1812 if (!_private || _private->closed)
1814 [self _cancelUpdateMouseoverTimer];
1815 [self _cancelUpdateFocusedAndActiveStateTimer];
1816 [self _clearLastHitViewIfSelf];
1817 // FIXME: This is slow; should remove individual observers instead.
1818 [[NSNotificationCenter defaultCenter] removeObserver:self];
1819 [_private->pluginController destroyAllPlugins];
1820 [_private->pluginController setDataSource:nil];
1821 // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1822 [self removeAllToolTips];
1824 _private->closed = YES;
1825 Page* page = core([self _webView]);
1827 page->dragController()->setDraggingImageURL(KURL());
1830 - (BOOL)_hasHTMLDocument
1832 Frame* coreFrame = core([self _frame]);
1835 Document* document = coreFrame->document();
1836 return document && document->isHTMLDocument();
1839 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1840 forType:(NSString *)pboardType
1841 inContext:(DOMRange *)context
1842 subresources:(NSArray **)subresources
1844 if (pboardType == WebArchivePboardType) {
1845 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1847 *subresources = [archive subresources];
1848 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1852 if (pboardType == NSFilenamesPboardType)
1853 return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1855 if (pboardType == NSHTMLPboardType) {
1856 NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1857 // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1858 if ([HTMLString hasPrefix:@"Version:"]) {
1859 NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
1860 if (range.location != NSNotFound)
1861 HTMLString = [HTMLString substringFromIndex:range.location];
1863 if ([HTMLString length] == 0)
1866 return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
1869 // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
1870 // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
1871 // FIXME: Remove this once bug 5052369 is fixed.
1872 if ([self _hasHTMLDocument] && pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType) {
1873 NSAttributedString *string = nil;
1874 if (pboardType == NSRTFDPboardType)
1875 string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
1877 string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
1881 NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
1882 [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
1883 self, @"WebResourceHandler", nil];
1886 BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
1887 if (!wasDeferringCallbacks)
1888 [[self _webView] setDefersCallbacks:YES];
1890 DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length])
1891 document:[[self _frame] DOMDocument]
1892 documentAttributes:documentAttributes
1897 NSEnumerator *e = [s objectEnumerator];
1899 while ((r = [e nextObject]))
1900 [[self _dataSource] addSubresource:r];
1902 if (!wasDeferringCallbacks)
1903 [[self _webView] setDefersCallbacks:NO];
1905 [documentAttributes release];
1909 if (pboardType == NSTIFFPboardType) {
1910 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
1911 URL:uniqueURLWithRelativePart(@"image.tiff")
1912 MIMEType:@"image/tiff"
1913 textEncodingName:nil
1915 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
1919 if (pboardType == NSPICTPboardType) {
1920 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
1921 URL:uniqueURLWithRelativePart(@"image.pict")
1922 MIMEType:@"image/pict"
1923 textEncodingName:nil
1925 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
1929 if (pboardType == NSURLPboardType) {
1930 NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
1931 DOMDocument* document = [[self _frame] DOMDocument];
1935 DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
1936 NSString *URLString = [URL _web_originalDataAsString];
1937 if ([URLString length] == 0)
1939 NSString *URLTitleString = [pasteboard stringForType:WebURLNamePboardType];
1940 DOMText *text = [document createTextNode:URLTitleString];
1941 [anchor setHref:URLString];
1942 [anchor appendChild:text];
1943 DOMDocumentFragment *fragment = [document createDocumentFragment];
1944 [fragment appendChild:anchor];
1947 if (pboardType == NSStringPboardType)
1948 return [[self _frame] _documentFragmentWithText:[pasteboard stringForType:NSStringPboardType]
1956 @implementation NSView (WebHTMLViewFileInternal)
1958 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
1960 unsigned count = [_subviews count];
1961 for (unsigned i = 0; i < count; ++i) {
1962 NSView *child = [_subviews objectAtIndex:i];
1963 if ([child isKindOfClass:[WebHTMLView class]])
1964 [array addObject:child];
1965 [child _web_addDescendantWebHTMLViewsToArray:array];
1971 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
1973 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
1975 if (object == nil) {
1976 [self removeObjectForKey:key];
1978 [self setObject:object forKey:key];
1984 #ifdef BUILDING_ON_TIGER
1986 // The following is a workaround for
1987 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
1988 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
1989 // Since the category will be searched before the real class, we'll prevent the flag from being
1990 // set on the tool tip panel.
1992 @interface NSToolTipPanel : NSPanel
1995 @interface NSToolTipPanel (WebHTMLViewFileInternal)
1998 @implementation NSToolTipPanel (WebHTMLViewFileInternal)
2000 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
2002 // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
2009 @interface NSArray (WebHTMLView)
2010 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
2013 @implementation WebHTMLView
2017 [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes]
2018 returnTypes:[[self class] _insertablePasteboardTypes]];
2019 #ifndef BUILDING_ON_TIGER
2020 WebCoreObjCFinalizeOnMainThread(self);
2024 - (id)initWithFrame:(NSRect)frame
2026 self = [super initWithFrame:frame];
2030 [self setFocusRingType:NSFocusRingTypeNone];
2032 // Make all drawing go through us instead of subviews.
2033 [self _setDrawsOwnDescendants:YES];
2035 _private = [[WebHTMLViewPrivate alloc] init];
2037 _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2038 _private->needsLayout = YES;
2045 // We can't assert that close has already been called because
2046 // this view can be removed from it's superview, even though
2047 // it could be needed later, so close if needed.
2056 ASSERT_MAIN_THREAD();
2057 // We can't assert that close has already been called because
2058 // this view can be removed from it's superview, even though
2059 // it could be needed later, so close if needed.
2064 // Returns YES if the delegate returns YES (so we should do no more work).
2065 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2067 BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2068 _private->selectorForDoCommandBySelector = 0;
2069 if (callerAlreadyCalledDelegate)
2071 WebView *webView = [self _webView];
2072 return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2075 static String commandNameForSelector(SEL selector)
2077 // Change a few command names into ones supported by WebCore::Editor.
2078 // If this list gets too long we might decide we need to use a hash table.
2079 if (selector == @selector(insertParagraphSeparator:) || selector == @selector(insertNewlineIgnoringFieldEditor:))
2080 return "InsertNewline";
2081 if (selector == @selector(insertTabIgnoringFieldEditor:))
2083 if (selector == @selector(pageDown:))
2084 return "MovePageDown";
2085 if (selector == @selector(pageDownAndModifySelection:))
2086 return "MovePageDownAndModifySelection";
2087 if (selector == @selector(pageUp:))
2088 return "MovePageUp";
2089 if (selector == @selector(pageUpAndModifySelection:))
2090 return "MovePageUpAndModifySelection";
2092 // Remove the trailing colon.
2093 const char* selectorName = sel_getName(selector);
2094 size_t selectorNameLength = strlen(selectorName);
2095 ASSERT(selectorNameLength >= 2);
2096 ASSERT(selectorName[selectorNameLength - 1] == ':');
2097 return String(selectorName, selectorNameLength - 1);
2100 - (Editor::Command)coreCommandBySelector:(SEL)selector
2102 Frame* coreFrame = core([self _frame]);
2104 return Editor::Command();
2105 return coreFrame->editor()->command(commandNameForSelector(selector));
2108 - (Editor::Command)coreCommandByName:(const char*)name
2110 Frame* coreFrame = core([self _frame]);
2112 return Editor::Command();
2113 return coreFrame->editor()->command(name);
2116 - (void)executeCoreCommandBySelector:(SEL)selector
2118 if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2120 [self coreCommandBySelector:selector].execute();
2123 - (void)executeCoreCommandByName:(const char*)name
2125 [self coreCommandByName:name].execute();
2128 // These commands are forwarded to the Editor object in WebCore.
2129 // Ideally we'd do this for all editing commands; more of the code
2130 // should be moved from here to there, and more commands should be
2131 // added to this list.
2133 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2134 // The functions are identical.
2136 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2138 WEBCORE_COMMAND(alignCenter)
2139 WEBCORE_COMMAND(alignJustified)
2140 WEBCORE_COMMAND(alignLeft)
2141 WEBCORE_COMMAND(alignRight)
2142 WEBCORE_COMMAND(copy)
2143 WEBCORE_COMMAND(cut)
2144 WEBCORE_COMMAND(delete)
2145 WEBCORE_COMMAND(deleteBackward)
2146 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2147 WEBCORE_COMMAND(deleteForward)
2148 WEBCORE_COMMAND(deleteToBeginningOfLine)
2149 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2150 WEBCORE_COMMAND(deleteToEndOfLine)
2151 WEBCORE_COMMAND(deleteToEndOfParagraph)
2152 WEBCORE_COMMAND(deleteToMark)
2153 WEBCORE_COMMAND(deleteWordBackward)
2154 WEBCORE_COMMAND(deleteWordForward)
2155 WEBCORE_COMMAND(indent)
2156 WEBCORE_COMMAND(insertBacktab)
2157 WEBCORE_COMMAND(insertLineBreak)
2158 WEBCORE_COMMAND(insertNewline)
2159 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2160 WEBCORE_COMMAND(insertParagraphSeparator)
2161 WEBCORE_COMMAND(insertTab)
2162 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2163 WEBCORE_COMMAND(moveBackward)
2164 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2165 WEBCORE_COMMAND(moveDown)
2166 WEBCORE_COMMAND(moveDownAndModifySelection)
2167 WEBCORE_COMMAND(moveForward)
2168 WEBCORE_COMMAND(moveForwardAndModifySelection)
2169 WEBCORE_COMMAND(moveLeft)
2170 WEBCORE_COMMAND(moveLeftAndModifySelection)
2171 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2172 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2173 WEBCORE_COMMAND(moveRight)
2174 WEBCORE_COMMAND(moveRightAndModifySelection)
2175 WEBCORE_COMMAND(moveToBeginningOfDocument)
2176 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2177 WEBCORE_COMMAND(moveToBeginningOfLine)
2178 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2179 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2180 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2181 WEBCORE_COMMAND(moveToBeginningOfSentence)
2182 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2183 WEBCORE_COMMAND(moveToEndOfDocument)
2184 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2185 WEBCORE_COMMAND(moveToEndOfLine)
2186 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2187 WEBCORE_COMMAND(moveToEndOfParagraph)
2188 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2189 WEBCORE_COMMAND(moveToEndOfSentence)
2190 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2191 WEBCORE_COMMAND(moveUp)
2192 WEBCORE_COMMAND(moveUpAndModifySelection)
2193 WEBCORE_COMMAND(moveWordBackward)
2194 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2195 WEBCORE_COMMAND(moveWordForward)
2196 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2197 WEBCORE_COMMAND(moveWordLeft)
2198 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2199 WEBCORE_COMMAND(moveWordRight)
2200 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2201 WEBCORE_COMMAND(outdent)
2202 WEBCORE_COMMAND(pageDown)
2203 WEBCORE_COMMAND(pageDownAndModifySelection)
2204 WEBCORE_COMMAND(pageUp)
2205 WEBCORE_COMMAND(pageUpAndModifySelection)
2206 WEBCORE_COMMAND(selectAll)
2207 WEBCORE_COMMAND(selectLine)
2208 WEBCORE_COMMAND(selectParagraph)
2209 WEBCORE_COMMAND(selectSentence)
2210 WEBCORE_COMMAND(selectToMark)
2211 WEBCORE_COMMAND(selectWord)
2212 WEBCORE_COMMAND(setMark)
2213 WEBCORE_COMMAND(subscript)
2214 WEBCORE_COMMAND(superscript)
2215 WEBCORE_COMMAND(swapWithMark)
2216 WEBCORE_COMMAND(transpose)
2217 WEBCORE_COMMAND(underline)
2218 WEBCORE_COMMAND(unscript)
2219 WEBCORE_COMMAND(yank)
2220 WEBCORE_COMMAND(yankAndSelect)
2222 #undef WEBCORE_COMMAND
2224 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2226 - (IBAction)takeFindStringFromSelection:(id)sender
2230 if (![self _hasSelection]) {
2235 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2238 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2240 [pasteboard declareTypes:types owner:[self _topHTMLView]];
2241 [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2245 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2247 Frame* coreFrame = core([self _frame]);
2250 if (coreFrame->selectionController()->isContentRichlyEditable())
2251 [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
2253 [self _pasteAsPlainTextWithPasteboard:pasteboard];
2257 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2259 if (sendType != nil && [[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]) {
2261 } else if (returnType != nil && [[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2264 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2267 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2268 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2269 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2270 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2271 // might be using the selector, and we don't want to break them.
2272 - (void)jumpToSelection:(id)sender
2276 if (Frame* coreFrame = core([self _frame]))
2277 coreFrame->revealSelection(RenderLayer::gAlignCenterAlways);
2280 - (NSCellStateValue)selectionHasStyle:(CSSStyleDeclaration*)style
2282 Frame* coreFrame = core([self _frame]);
2285 return kit(coreFrame->editor()->selectionHasStyle(style));
2288 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2290 SEL action = [item action];
2291 RefPtr<Frame> frame = core([self _frame]);
2296 if (Document* doc = frame->document()) {
2297 if (doc->isPluginDocument())
2299 if (doc->isImageDocument()) {
2300 if (action == @selector(copy:))
2301 return frame->loader()->isComplete();
2306 if (action == @selector(changeSpelling:)
2307 || action == @selector(_changeSpellingFromMenu:)
2308 || action == @selector(checkSpelling:)
2309 || action == @selector(complete:)
2310 || action == @selector(pasteFont:))
2311 return [self _canEdit];
2313 if (action == @selector(showGuessPanel:)) {
2314 #ifndef BUILDING_ON_TIGER
2315 // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2316 NSMenuItem *menuItem = (NSMenuItem *)item;
2317 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2318 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2319 [menuItem setTitle:panelShowing ? UI_STRING("Hide Spelling and Grammar", "menu item title") : UI_STRING("Show Spelling and Grammar", "menu item title")];
2322 return [self _canEdit];
2325 if (action == @selector(changeBaseWritingDirection:)) {
2326 NSWritingDirection writingDirection = static_cast<NSWritingDirection>([item tag]);
2327 if (writingDirection == NSWritingDirectionNatural)
2329 NSMenuItem *menuItem = (NSMenuItem *)item;
2330 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2331 RefPtr<CSSStyleDeclaration> style = new CSSMutableStyleDeclaration;
2333 style->setProperty("direction", writingDirection == NSWritingDirectionLeftToRight ? "LTR" : "RTL", ec);
2334 [menuItem setState:frame->editor()->selectionHasStyle(style.get())];
2336 return [self _canEdit];
2339 if (action == @selector(toggleBaseWritingDirection:)) {
2340 NSMenuItem *menuItem = (NSMenuItem *)item;
2341 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2342 RefPtr<CSSStyleDeclaration> style = new CSSMutableStyleDeclaration;
2344 style->setProperty("direction", "RTL", ec);
2345 // Take control of the title of the menu item, instead of just checking/unchecking it because otherwise
2346 // we don't know what the check would mean.
2347 [menuItem setTitle:frame->editor()->selectionHasStyle(style.get())
2348 ? UI_STRING("Left to Right", "Left to Right context menu item")
2349 : UI_STRING("Right to Left", "Right to Left context menu item")];
2351 return [self _canEdit];
2354 if (action == @selector(changeAttributes:)
2355 || action == @selector(changeColor:)
2356 || action == @selector(changeFont:))
2357 return [self _canEditRichly];
2359 if (action == @selector(capitalizeWord:)
2360 || action == @selector(lowercaseWord:)
2361 || action == @selector(uppercaseWord:))
2362 return [self _hasSelection] && [self _isEditable];
2364 if (action == @selector(centerSelectionInVisibleArea:)
2365 || action == @selector(jumpToSelection:)
2366 || action == @selector(copyFont:))
2367 return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2369 if (action == @selector(changeDocumentBackgroundColor:))
2370 return [[self _webView] isEditable] && [self _canEditRichly];
2372 if (action == @selector(_ignoreSpellingFromMenu:)
2373 || action == @selector(_learnSpellingFromMenu:)
2374 || action == @selector(takeFindStringFromSelection:))
2375 return [self _hasSelection];
2377 if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2378 return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste());
2380 if (action == @selector(pasteAsRichText:))
2381 return frame && (frame->editor()->canDHTMLPaste()
2382 || (frame->editor()->canPaste() && frame->selectionController()->isContentRichlyEditable()));
2384 if (action == @selector(performFindPanelAction:))
2387 if (action == @selector(_lookUpInDictionaryFromMenu:))
2388 return [self _hasSelection];
2390 #ifndef BUILDING_ON_TIGER
2391 if (action == @selector(toggleGrammarChecking:)) {
2392 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate
2393 // the selector here because we implement it here, and we must implement it here because the AppKit
2394 // code checks the first responder.
2395 NSMenuItem *menuItem = (NSMenuItem *)item;
2396 if ([menuItem isKindOfClass:[NSMenuItem class]])
2397 [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2402 Editor::Command command = [self coreCommandBySelector:action];
2403 if (command.isSupported()) {
2404 NSMenuItem *menuItem = (NSMenuItem *)item;
2405 if ([menuItem isKindOfClass:[NSMenuItem class]])
2406 [menuItem setState:kit(command.state())];
2407 return command.isEnabled();
2413 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2415 BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2416 return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2419 - (BOOL)acceptsFirstResponder
2421 // Don't accept first responder when we first click on this view.
2422 // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2423 // Do accept first responder at any other time, for example from keyboard events,
2424 // or from calls back from WebCore once we begin mouse-down event handling.
2425 NSEvent *event = [NSApp currentEvent];
2426 if ([event type] == NSLeftMouseDown
2427 && !_private->handlingMouseDownEvent
2428 && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2434 - (BOOL)maintainsInactiveSelection
2436 // This method helps to determine whether the WebHTMLView should maintain
2437 // an inactive selection when it's not first responder.
2438 // Traditionally, these views have not maintained such selections,
2439 // clearing them when the view was not first responder. However,
2440 // to fix bugs like this one:
2441 // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even
2442 // when they're not firstResponder"
2443 // it was decided to add a switch to act more like an NSTextView.
2445 if ([[self _webView] maintainsInactiveSelection])
2448 // Predict the case where we are losing first responder status only to
2449 // gain it back again. Want to keep the selection in that case.
2450 id nextResponder = [[self window] _newFirstResponderAfterResigning];
2451 if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2452 id contentView = [nextResponder contentView];
2454 nextResponder = contentView;
2456 if ([nextResponder isKindOfClass:[NSClipView class]]) {
2457 id documentView = [nextResponder documentView];
2459 nextResponder = documentView;
2461 if (nextResponder == self)
2464 Frame* coreFrame = core([self _frame]);
2465 bool selectionIsEditable = coreFrame && coreFrame->selectionController()->isContentEditable();
2466 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2467 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2469 return selectionIsEditable && nextResponderIsInWebView;
2472 - (void)addMouseMovedObserver
2474 if (!_private->dataSource || ![self _isTopHTMLView])
2477 // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2478 if (!([[self window] isKeyWindow] || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]))
2481 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2482 name:WKMouseMovedNotification() object:nil];
2483 [self _frameOrBoundsChanged];
2486 - (void)removeMouseMovedObserverUnconditionally
2488 [[NSNotificationCenter defaultCenter] removeObserver:self
2489 name:WKMouseMovedNotification() object:nil];
2492 - (void)removeMouseMovedObserver
2494 // Don't remove the observer if we're running the Dashboard.
2495 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2498 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2499 [self removeMouseMovedObserverUnconditionally];
2502 - (void)addSuperviewObservers
2504 // We watch the bounds of our superview, so that we can do a layout when the size
2505 // of the superview changes. This is different from other scrollable things that don't
2506 // need this kind of thing because their layout doesn't change.
2508 // We need to pay attention to both height and width because our "layout" has to change
2509 // to extend the background the full height of the space and because some elements have
2510 // sizes that are based on the total size of the view.
2512 NSView *superview = [self superview];
2513 if (superview && [self window]) {
2514 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged)
2515 name:NSViewFrameDidChangeNotification object:superview];
2516 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged)
2517 name:NSViewBoundsDidChangeNotification object:superview];
2519 // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2520 // It will check the current size/scroll against the previous layout's size/scroll. We need to
2521 // do this here to catch the case where the WebView is laid out at one size, removed from its
2522 // window, resized, and inserted into another window. Our frame/bounds changed notifications
2523 // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2524 [self _frameOrBoundsChanged];
2528 - (void)removeSuperviewObservers
2530 NSView *superview = [self superview];
2531 if (superview && [self window]) {
2532 [[NSNotificationCenter defaultCenter] removeObserver:self
2533 name:NSViewFrameDidChangeNotification object:superview];
2534 [[NSNotificationCenter defaultCenter] removeObserver:self
2535 name:NSViewBoundsDidChangeNotification object:superview];
2539 - (void)addWindowObservers
2541 NSWindow *window = [self window];
2543 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:)
2544 name:NSWindowDidBecomeKeyNotification object:nil];
2545 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:)
2546 name:NSWindowDidResignKeyNotification object:nil];
2547 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:)
2548 name:NSWindowWillCloseNotification object:window];
2552 - (void)removeWindowObservers
2554 NSWindow *window = [self window];
2556 [[NSNotificationCenter defaultCenter] removeObserver:self
2557 name:NSWindowDidBecomeKeyNotification object:nil];
2558 [[NSNotificationCenter defaultCenter] removeObserver:self
2559 name:NSWindowDidResignKeyNotification object:nil];
2560 [[NSNotificationCenter defaultCenter] removeObserver:self
2561 name:NSWindowWillCloseNotification object:window];
2565 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2567 [self removeSuperviewObservers];
2570 - (void)viewDidMoveToSuperview
2572 if ([self superview] != nil)
2573 [self addSuperviewObservers];
2576 static void _updateFocusedAndActiveStateTimerCallback(CFRunLoopTimerRef timer, void *info)
2578 WebHTMLView *view = (WebHTMLView *)info;
2579 [view _updateFocusedAndActiveState];
2582 - (void)viewWillMoveToWindow:(NSWindow *)window
2584 // Don't do anything if we aren't initialized. This happens
2585 // when decoding a WebView. When WebViews are decoded their subviews
2586 // are created by initWithCoder: and so won't be normally
2587 // initialized. The stub views are discarded by WebView.
2591 // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2592 [self removeMouseMovedObserverUnconditionally];
2593 [self removeWindowObservers];
2594 [self removeSuperviewObservers];
2595 [self _cancelUpdateMouseoverTimer];
2596 [self _cancelUpdateFocusedAndActiveStateTimer];
2598 [[self _pluginController] stopAllPlugins];
2601 - (void)viewDidMoveToWindow
2603 // Don't do anything if we aren't initialized. This happens
2604 // when decoding a WebView. When WebViews are decoded their subviews
2605 // are created by initWithCoder: and so won't be normally
2606 // initialized. The stub views are discarded by WebView.
2607 if (!_private || _private->closed)
2610 [self _stopAutoscrollTimer];
2611 if ([self window]) {
2612 _private->lastScrollPosition = [[self superview] bounds].origin;
2613 [self addWindowObservers];
2614 [self addSuperviewObservers];
2615 [self addMouseMovedObserver];
2617 // Schedule this update, rather than making the call right now.
2618 // The reason is that placing the caret in the just-installed view requires
2619 // the HTML/XML document to be available on the WebCore side, but it is not
2620 // at the time this code is running. However, it will be there on the next
2621 // crank of the run loop. Doing this helps to make a blinking caret appear
2622 // in a new, empty window "automatic".
2623 if (!_private->updateFocusedAndActiveStateTimer) {
2624 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
2625 _private->updateFocusedAndActiveStateTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0,
2626 _updateFocusedAndActiveStateTimerCallback, &context);
2627 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateFocusedAndActiveStateTimer, kCFRunLoopDefaultMode);
2630 [[self _pluginController] startAllPlugins];
2632 _private->lastScrollPosition = NSZeroPoint;
2636 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2638 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2641 - (void)viewDidMoveToHostWindow
2643 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2647 - (void)addSubview:(NSView *)view
2649 [super addSubview:view];
2651 if ([WebPluginController isPlugInView:view])
2652 [[self _pluginController] addPlugin:view];
2655 - (void)willRemoveSubview:(NSView *)subview
2657 if ([WebPluginController isPlugInView:subview])
2658 [[self _pluginController] destroyPlugin:subview];
2660 [super willRemoveSubview:subview];
2663 - (void)reapplyStyles
2665 if (!_private->needsToApplyStyles)
2669 double start = CFAbsoluteTimeGetCurrent();
2672 Frame* coreFrame = core([self _frame]);
2673 if (FrameView* coreView = coreFrame->view())
2674 coreView->setMediaType(_private->printing ? "print" : "screen");
2675 if (Document* document = coreFrame->document())
2676 document->setPrinting(_private->printing);
2677 coreFrame->reapplyStyles();
2680 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2681 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2684 _private->needsToApplyStyles = NO;
2687 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2688 // minPageWidth==0 implies a non-printing layout
2689 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
2691 [self reapplyStyles];
2693 if (!_private->needsLayout && ![[self _frame] _needsLayout])
2697 double start = CFAbsoluteTimeGetCurrent();
2700 LOG(View, "%@ doing layout", self);
2702 if (minPageWidth > 0.0) {
2703 [[self _frame] _forceLayoutWithMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
2705 [[self _frame] _forceLayoutAdjustingViewSize:adjustViewSize];
2707 _private->needsLayout = NO;
2709 if (!_private->printing)
2710 _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
2713 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2714 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
2720 [self layoutToMinimumPageWidth:0.0f maximumPageWidth:0.0f adjustingViewSize:NO];
2723 - (NSMenu *)menuForEvent:(NSEvent *)event
2725 [_private->compController endRevertingChange:NO moveLeft:NO];
2727 _private->handlingMouseDownEvent = YES;
2728 BOOL handledEvent = NO;
2729 Frame* coreFrame = core([self _frame]);
2732 _private->handlingMouseDownEvent = NO;
2736 Page* page = coreFrame->page();
2740 page->contextMenuController()->clearContextMenu();
2741 // Match behavior of other browsers by sending an onmousedown event for right clicks.
2742 coreFrame->eventHandler()->mouseDown(event);
2743 handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformMouseEvent(event));
2744 _private->handlingMouseDownEvent = NO;
2749 ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
2753 NSArray* menuItems = coreMenu->platformDescription();
2755 if (menuItems && [menuItems count] > 0) {
2756 menu = [[[NSMenu alloc] init] autorelease];
2757 for (unsigned i = 0; i < [menuItems count]; i++)
2758 [menu addItem:[menuItems objectAtIndex:i]];
2764 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
2766 return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
2771 Frame* coreFrame = core([self _frame]);
2774 Document* document = coreFrame->document();
2778 document->setFocusedNode(0);
2783 return [[self _webView] drawsBackground];
2786 - (void)setNeedsDisplay:(BOOL)flag
2788 LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO");
2789 [super setNeedsDisplay:flag];
2792 - (void)setNeedsLayout: (BOOL)flag
2794 LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO");
2795 _private->needsLayout = flag;
2798 - (void)setNeedsToApplyStyles: (BOOL)flag
2800 LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO");
2801 _private->needsToApplyStyles = flag;
2804 - (void)drawSingleRect:(NSRect)rect
2806 [NSGraphicsContext saveGraphicsState];
2809 ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
2811 [(WebClipView *)[self superview] setAdditionalClip:rect];
2814 if ([self _transparentBackground]) {
2815 [[NSColor clearColor] set];
2819 [[self _frame] _drawRect:rect];
2821 // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
2822 // called after the WebView has closed. If the client did not properly close the WebView and set the
2823 // UIDelegate to nil, then the UIDelegate will be stale and this code will crash.
2824 static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
2825 if (version3OrLaterClient) {
2826 WebView *webView = [self _webView];
2827 [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
2830 [(WebClipView *)[self superview] resetAdditionalClip];
2832 [NSGraphicsContext restoreGraphicsState];
2833 } @catch (NSException *localException) {
2834 [(WebClipView *)[self superview] resetAdditionalClip];
2835 [NSGraphicsContext restoreGraphicsState];
2836 LOG_ERROR("Exception caught while drawing: %@", localException);
2837 [localException raise];
2841 - (void)drawRect:(NSRect)rect
2843 ASSERT_MAIN_THREAD();
2844 LOG(View, "%@ drawing", self);
2846 const NSRect *rects;
2848 [self getRectsBeingDrawn:&rects count:&count];
2850 BOOL subviewsWereSetAside = _private->subviewsSetAside;
2851 if (subviewsWereSetAside)
2852 [self _restoreSubviews];
2855 double start = CFAbsoluteTimeGetCurrent();
2858 // If count == 0 here, use the rect passed in for drawing. This is a workaround for:
2859 // <rdar://problem/3908282> REGRESSION (Mail): No drag image dragging selected text in Blot and Mail
2860 // The reason for the workaround is that this method is called explicitly from the code
2861 // to generate a drag image, and at that time, getRectsBeingDrawn:count: will return a zero count.
2862 const int cRectThreshold = 10;
2863 const float cWastedSpaceThreshold = 0.75f;
2864 BOOL useUnionedRect = (count <= 1) || (count > cRectThreshold);
2865 if (!useUnionedRect) {
2866 // Attempt to guess whether or not we should use the unioned rect or the individual rects.
2867 // We do this by computing the percentage of "wasted space" in the union. If that wasted space
2868 // is too large, then we will do individual rect painting instead.
2869 float unionPixels = (rect.size.width * rect.size.height);
2870 float singlePixels = 0;
2871 for (int i = 0; i < count; ++i)
2872 singlePixels += rects[i].size.width * rects[i].size.height;
2873 float wastedSpace = 1 - (singlePixels / unionPixels);
2874 if (wastedSpace <= cWastedSpaceThreshold)
2875 useUnionedRect = YES;
2879 [self drawSingleRect:rect];
2881 for (int i = 0; i < count; ++i)
2882 [self drawSingleRect:rects[i]];
2885 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2886 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
2889 if (subviewsWereSetAside)
2890 [self _setAsideSubviews];
2893 // Turn off the additional clip while computing our visibleRect.
2894 - (NSRect)visibleRect
2896 if (!([[self superview] isKindOfClass:[WebClipView class]]))
2897 return [super visibleRect];
2899 WebClipView *clipView = (WebClipView *)[self superview];
2901 BOOL hasAdditionalClip = [clipView hasAdditionalClip];
2902 if (!hasAdditionalClip) {
2903 return [super visibleRect];
2906 NSRect additionalClip = [clipView additionalClip];
2907 [clipView resetAdditionalClip];
2908 NSRect visibleRect = [super visibleRect];
2909 [clipView setAdditionalClip:additionalClip];
2918 - (void)windowDidBecomeKey:(NSNotification *)notification
2920 NSWindow *keyWindow = [notification object];
2922 if (keyWindow == [self window])
2923 [self addMouseMovedObserver];
2925 if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
2926 [self _updateFocusedAndActiveState];
2929 - (void)windowDidResignKey:(NSNotification *)notification
2931 NSWindow *formerKeyWindow = [notification object];
2933 if (formerKeyWindow == [self window])
2934 [self removeMouseMovedObserver];
2936 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
2937 [self _updateFocusedAndActiveState];
2938 [_private->compController endRevertingChange:NO moveLeft:NO];
2942 - (void)windowWillClose:(NSNotification *)notification
2944 [_private->compController endRevertingChange:NO moveLeft:NO];
2945 [[self _pluginController] destroyAllPlugins];
2948 - (void)scrollWheel:(NSEvent *)event
2951 Frame* frame = core([self _frame]);
2952 if (!frame || !frame->eventHandler()->wheelEvent(event))
2953 [super scrollWheel:event];
2957 - (BOOL)_isSelectionEvent:(NSEvent *)event
2959 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2960 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
2963 - (BOOL)acceptsFirstMouse:(NSEvent *)event
2965 NSView *hitView = [self _hitViewForEvent:event];
2966 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2968 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
2972 bool result = false;
2973 if (Frame* coreFrame = core([hitHTMLView _frame])) {
2974 coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
2975 [hitHTMLView _setMouseDownEvent:event];
2976 if ([hitHTMLView _isSelectionEvent:event])
2977 result = coreFrame->eventHandler()->eventMayStartDrag(event);
2978 [hitHTMLView _setMouseDownEvent:nil];
2982 return [hitView acceptsFirstMouse:event];
2985 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
2987 NSView *hitView = [self _hitViewForEvent:event];
2988 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2990 bool result = false;
2991 if ([hitHTMLView _isSelectionEvent:event])
2992 if (Frame* coreFrame = core([hitHTMLView _frame])) {
2993 [hitHTMLView _setMouseDownEvent:event];
2994 result = coreFrame->eventHandler()->eventMayStartDrag(event);
2995 [hitHTMLView _setMouseDownEvent:nil];
2999 return [hitView shouldDelayWindowOrderingForEvent:event];
3002 - (void)mouseDown:(NSEvent *)event
3004 RetainPtr<WebHTMLView> protector = self;
3005 if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
3008 _private->handlingMouseDownEvent = YES;
3010 // Record the mouse down position so we can determine drag hysteresis.
3011 [self _setMouseDownEvent:event];
3013 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3014 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3017 [_private->compController endRevertingChange:NO moveLeft:NO];
3019 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
3020 // We don't want to pass them along to KHTML a second time.
3021 if (!([event modifierFlags] & NSControlKeyMask)) {
3022 _private->ignoringMouseDraggedEvents = NO;
3024 // Don't do any mouseover while the mouse is down.
3025 [self _cancelUpdateMouseoverTimer];
3027 // Let WebCore get a chance to deal with the event. This will call back to us
3028 // to start the autoscroll timer if appropriate.
3029 if (Frame* coreframe = core([self _frame]))
3030 coreframe->eventHandler()->mouseDown(event);
3034 _private->handlingMouseDownEvent = NO;
3037 - (void)dragImage:(NSImage *)dragImage
3039 offset:(NSSize)offset
3040 event:(NSEvent *)event
3041 pasteboard:(NSPasteboard *)pasteboard
3043 slideBack:(BOOL)slideBack
3045 ASSERT(self == [self _topHTMLView]);
3046 [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
3049 - (void)mouseDragged:(NSEvent *)event
3051 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3052 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3057 if (!_private->ignoringMouseDraggedEvents)
3058 if (Frame* coreframe = core([self _frame]))
3059 coreframe->eventHandler()->mouseDragged(event);
3064 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
3066 ASSERT(![self _webView] || [self _isTopHTMLView]);
3068 Page *page = core([self _webView]);
3071 return NSDragOperationNone;
3073 if (page->dragController()->dragOperation() == DragOperationNone)
3074 return NSDragOperationGeneric | NSDragOperationCopy;
3076 return (NSDragOperation)page->dragController()->dragOperation();
3079 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
3081 ASSERT(![self _webView] || [self _isTopHTMLView]);
3083 NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
3084 NSPoint windowMouseLoc = windowImageLoc;
3086 if (Page* page = core([self _webView])) {
3087 DragController* dragController = page->dragController();
3088 NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3091 [[self _frame] _dragSourceMovedTo:windowMouseLoc];
3094 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
3096 ASSERT(![self _webView] || [self _isTopHTMLView]);
3098 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
3099 NSPoint windowMouseLoc = windowImageLoc;
3101 if (Page* page = core([self _webView])) {
3102 DragController* dragController = page->dragController();
3103 windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3104 dragController->dragEnded();
3107 [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation];
3109 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3110 _private->ignoringMouseDraggedEvents = YES;
3112 // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
3113 // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
3114 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
3115 location:windowMouseLoc
3116 modifierFlags:[[NSApp currentEvent] modifierFlags]
3117 timestamp:[NSDate timeIntervalSinceReferenceDate]
3118 windowNumber:[[self window] windowNumber]
3119 context:[[NSApp currentEvent] context]
3120 eventNumber:0 clickCount:0 pressure:0];
3121 [self mouseUp:fakeEvent]; // This will also update the mouseover state.
3124 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3126 NSFileWrapper *wrapper = nil;
3127 NSURL *draggingImageURL = nil;
3129 if (WebCore::CachedResource* tiffResource = [self promisedDragTIFFDataSource]) {
3131 SharedBuffer *buffer = tiffResource->data();
3133 goto noPromisedData;
3135 NSData *data = buffer->createNSData();
3136 NSURLResponse *response = tiffResource->response().nsURLResponse();
3137 draggingImageURL = [response URL];
3138 wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
3139 [wrapper setPreferredFilename:[response suggestedFilename]];
3145 ASSERT(![self _webView] || [self _isTopHTMLView]);
3146 Page* page = core([self _webView]);
3148 //If a load occurs midway through a drag, the view may be detached, which gives
3149 //us no ability to get to the original Page, so we cannot access any drag state
3150 //FIXME: is there a way to recover?
3154 KURL imageURL = page->dragController()->draggingImageURL();
3155 ASSERT(!imageURL.isEmpty());
3156 draggingImageURL = imageURL;
3158 wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL];
3161 if (wrapper == nil) {
3162 LOG_ERROR("Failed to create image file.");
3166 // FIXME: Report an error if we fail to create a file.
3167 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3168 path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
3169 if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
3170 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
3172 if (draggingImageURL)
3173 [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path];
3175 return [NSArray arrayWithObject:[path lastPathComponent]];
3178 - (void)mouseUp:(NSEvent *)event
3180 [self _setMouseDownEvent:nil];
3182 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3183 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3188 [self _stopAutoscrollTimer];
3189 if (Frame* coreframe = core([self _frame]))
3190 coreframe->eventHandler()->mouseUp(event);
3191 [self _updateMouseoverWithFakeEvent];
3196 - (void)mouseMovedNotification:(NSNotification *)notification
3198 [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
3201 // returning YES from this method is the way we tell AppKit that it is ok for this view
3202 // to be in the key loop even when "tab to all controls" is not on.
3203 - (BOOL)needsPanelToBecomeKey
3208 - (BOOL)becomeFirstResponder
3210 NSSelectionDirection direction = NSDirectSelection;
3211 if (![[self _webView] _isPerformingProgrammaticFocus])
3212 direction = [[self window] keyViewSelectionDirection];
3214 [self _updateFocusedAndActiveState];
3215 [self _updateFontPanel];
3217 Frame* frame = core([self _frame]);
3221 frame->editor()->setStartNewKillRingSequence(true);
3223 if (direction == NSDirectSelection)
3226 Page* page = frame->page();
3230 page->focusController()->setFocusedFrame(frame);
3231 if (Document* document = frame->document())
3232 document->setFocusedNode(0);
3233 page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward,
3234 frame->eventHandler()->currentKeyboardEvent().get());
3238 - (BOOL)resignFirstResponder
3240 BOOL resign = [super resignFirstResponder];
3242 [_private->compController endRevertingChange:NO moveLeft:NO];
3243 _private->resigningFirstResponder = YES;
3244 if (![self maintainsInactiveSelection]) {
3246 if (![[self _webView] _isPerformingProgrammaticFocus])
3249 [self _updateFocusedAndActiveState];
3250 _private->resigningFirstResponder = NO;
3255 - (void)setDataSource:(WebDataSource *)dataSource
3258 if (_private->dataSource != dataSource) {
3259 ASSERT(!_private->closed);
3260 BOOL hadDataSource = _private->dataSource != nil;
3262 [dataSource retain];
3263 [_private->dataSource release];
3264 _private->dataSource = dataSource;
3265 [_private->pluginController setDataSource:dataSource];
3268 [self addMouseMovedObserver];
3272 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3276 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3277 // key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3278 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3279 - (void)updateCell:(NSCell*)cell
3283 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3284 // pageWidth != 0 implies we will relayout to a new width
3285 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
3287 WebFrame *frame = [self _frame];
3288 NSArray *subframes = [frame childFrames];
3289 unsigned n = [subframes count];
3291 for (i = 0; i != n; ++i) {
3292 WebFrame *subframe = [subframes objectAtIndex:i];
3293 WebFrameView *frameView = [subframe frameView];
3294 if ([[subframe _dataSource] _isDocumentHTML]) {
3295 [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:adjustViewSize];
3299 if (printing != _private->printing) {
3300 [_private->pageRects release];
3301 _private->pageRects = nil;
3302 _private->printing = printing;
3304 _private->avoidingPrintOrphan = NO;
3305 [self setNeedsToApplyStyles:YES];
3306 [self setNeedsLayout:YES];
3307 [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
3309 // Can't do this when starting printing or nested printing won't work, see 3491427.
3310 [self setNeedsDisplay:NO];
3315 - (BOOL)canPrintHeadersAndFooters
3320 // This is needed for the case where the webview is embedded in the view that's being printed.
3321 // It shouldn't be called when the webview is being printed directly.
3322 - (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit
3324 // This helps when we print as part of a larger print process.
3325 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3326 BOOL wasInPrintingMode = _private->printing;
3327 if (!wasInPrintingMode)
3328 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3330 float newBottomFloat = *newBottom;
3331 [[self _frame] _adjustPageHeightNew:&newBottomFloat top:oldTop bottom:oldBottom limit:bottomLimit];
3332 *newBottom = newBottomFloat;
3334 if (!wasInPrintingMode) {
3335 NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation];
3336 if (currenPrintOperation)
3337 // delay _setPrinting:NO until back to main loop as this method may get called repeatedly
3338 [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0];
3340 // not sure if this is actually ever invoked, it probably shouldn't be
3341 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3345 - (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
3347 NSPrintInfo *printInfo = [printOperation printInfo];
3348 return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
3351 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3353 float viewWidth = NSWidth([self bounds]);
3354 if (viewWidth < 1) {
3355 LOG_ERROR("%@ has no width when printing", self);
3359 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3360 float maxShrinkToFitScaleFactor = 1.0f / PrintingMaximumShrinkFactor;
3361 float shrinkToFitScaleFactor = [self _availablePaperWidthForPrintOperation:printOperation]/viewWidth;
3362 float shrinkToAvoidOrphan = _private->avoidingPrintOrphan ? (1.0f / PrintingOrphanShrinkAdjustment) : 1.0f;
3363 return userScaleFactor * MAX(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor) * shrinkToAvoidOrphan;
3366 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3367 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3368 // if AppKit makes it SPI/API.
3369 - (float)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3371 return [self _scaleFactorForPrintOperation:printOperation];
3374 // This is used for Carbon printing. At some point we might want to make this public API.
3375 - (void)setPageWidthForPrinting:(float)pageWidth
3377 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3378 [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
3381 - (void)_endPrintMode
3383 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
3384 [[self window] setAutodisplay:YES];
3387 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3389 ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3390 NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3391 if (initiatingOperation == currentOperation) {
3392 // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3393 // being extra paranoid here since the printing code is so fragile. Delay the cleanup
3395 ASSERT_NOT_REACHED();
3396 [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3397 } else if ([currentOperation view] == self) {
3398 // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3399 // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3400 // fragile. Do nothing, because we don't want to break the print job currently in progress, and
3401 // the print job currently in progress is responsible for its own cleanup.
3402 ASSERT_NOT_REACHED();
3404 // The print job that kicked off this delayed call has finished, and this view is not being
3405 // printed again. We expect that no other print job has started. Since this delayed call wasn't
3406 // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3407 // the print mode here.
3408 ASSERT(currentOperation == nil);
3409 [self _endPrintMode];
3413 // Return the number of pages available for printing
3414 - (BOOL)knowsPageRange:(NSRangePointer)range
3416 // Must do this explicit display here, because otherwise the view might redisplay while the print
3417 // sheet was up, using printer fonts (and looking different).
3418 [self displayIfNeeded];
3419 [[self window] setAutodisplay:NO];
3421 // If we are a frameset just print with the layout we have onscreen, otherwise relayout
3422 // according to the paper size
3423 float minLayoutWidth = 0.0f;
3424 float maxLayoutWidth = 0.0f;
3425 Frame* frame = core([self _frame]);
3428 if (!frame->isFrameSet()) {
3429 float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
3430 minLayoutWidth = paperWidth * PrintingMinimumShrinkFactor;
3431 maxLayoutWidth = paperWidth * PrintingMaximumShrinkFactor;
3433 [self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; // will relayout
3434 NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3435 // Certain types of errors, including invalid page ranges, can cause beginDocument and
3436 // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3437 // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3438 // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3439 // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3440 // which is after beginDocument and endDocument would be called.
3441 [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3442 [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3444 // There is a theoretical chance that someone could do some drawing between here and endDocument,
3445 // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3446 // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3448 range->location = 1;
3449 float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3450 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3451 [_private->pageRects release];
3452 float fullPageHeight = floorf([self _calculatePrintHeight]/totalScaleFactor);
3453 NSArray *newPageRects = [[self _frame] _computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3454 printHeight:fullPageHeight];
3456 // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3457 // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3458 // a blank page (with correct-looking header and footer if that option is on), which matches
3459 // the behavior of IE and Camino at least.
3460 if ([newPageRects count] == 0)
3461 newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3462 else if ([newPageRects count] > 1) {
3463 // If the last page is a short orphan, try adjusting the print height slightly to see if this will squeeze the
3464 // content onto one fewer page. If it does, use the adjusted scale. If not, use the original scale.
3465 float lastPageHeight = NSHeight([[newPageRects lastObject] rectValue]);
3466 if (lastPageHeight/fullPageHeight < LastPrintedPageOrphanRatio) {
3467 NSArray *adjustedPageRects = [[self _frame] _computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3468 printHeight:fullPageHeight*PrintingOrphanShrinkAdjustment];
3469 // Use the adjusted rects only if the page count went down
3470 if ([adjustedPageRects count] < [newPageRects count]) {
3471 newPageRects = adjustedPageRects;
3472 _private->avoidingPrintOrphan = YES;
3477 _private->pageRects = [newPageRects retain];
3479 range->length = [_private->pageRects count];
3484 // Return the drawing rectangle for a particular page number
3485 - (NSRect)rectForPage:(NSInteger)page
3487 return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3490 - (void)drawPageBorderWithSize:(NSSize)borderSize
3492 ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));
3493 [[self _webView] _drawHeaderAndFooter];
3496 - (void)beginDocument
3499 // From now on we'll get a chance to call _endPrintMode in either beginDocument or
3500 // endDocument, so we can cancel the "just in case" pending call.
3501 [NSObject cancelPreviousPerformRequestsWithTarget:self
3502 selector:@selector(_delayedEndPrintMode:)
3503 object:[NSPrintOperation currentOperation]];
3504 [super beginDocument];
3505 } @catch (NSException *localException) {