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 "WebNetscapePluginView.h"
36 #import "WebClipView.h"
37 #import "WebDOMOperationsPrivate.h"
38 #import "WebDataSourceInternal.h"
39 #import "WebDefaultUIDelegate.h"
40 #import "WebDocumentInternal.h"
41 #import "WebDynamicScrollBarsView.h"
42 #import "WebEditingDelegate.h"
43 #import "WebElementDictionary.h"
44 #import "WebFrameInternal.h"
45 #import "WebFramePrivate.h"
46 #import "WebFrameViewInternal.h"
47 #import "WebHTMLRepresentationPrivate.h"
48 #import "WebHTMLViewInternal.h"
49 #import "WebKitLogging.h"
50 #import "WebKitNSStringExtras.h"
51 #import "WebKitVersionChecks.h"
52 #import "WebLocalizableStrings.h"
53 #import "WebNodeHighlight.h"
54 #import "WebNSAttributedStringExtras.h"
55 #import "WebNSEventExtras.h"
56 #import "WebNSFileManagerExtras.h"
57 #import "WebNSImageExtras.h"
58 #import "WebNSObjectExtras.h"
59 #import "WebNSPasteboardExtras.h"
60 #import "WebNSPrintOperationExtras.h"
61 #import "WebNSURLExtras.h"
62 #import "WebNSViewExtras.h"
63 #import "WebPluginController.h"
64 #import "WebPreferences.h"
65 #import "WebPreferencesPrivate.h"
66 #import "WebResourcePrivate.h"
67 #import "WebStringTruncator.h"
68 #import "WebTypesInternal.h"
69 #import "WebUIDelegatePrivate.h"
70 #import "WebViewInternal.h"
71 #import <AppKit/NSAccessibility.h>
72 #import <ApplicationServices/ApplicationServices.h>
74 #import <WebCore/CachedImage.h>
75 #import <WebCore/CachedResourceClient.h>
76 #import <WebCore/ColorMac.h>
77 #import <WebCore/ContextMenu.h>
78 #import <WebCore/ContextMenuController.h>
79 #import <WebCore/Document.h>
80 #import <WebCore/DocumentFragment.h>
81 #import <WebCore/Editor.h>
82 #import <WebCore/EditorDeleteAction.h>
83 #import <WebCore/Element.h>
84 #import <WebCore/EventHandler.h>
85 #import <WebCore/ExceptionHandlers.h>
86 #import <WebCore/DragController.h>
87 #import <WebCore/FloatRect.h>
88 #import <WebCore/FocusController.h>
89 #import <WebCore/Frame.h>
90 #import <WebCore/FrameLoader.h>
91 #import <WebCore/FrameView.h>
92 #import <WebCore/HitTestResult.h>
93 #import <WebCore/HTMLNames.h>
94 #import <WebCore/Image.h>
95 #import <WebCore/KeyboardEvent.h>
96 #import <WebCore/LegacyWebArchive.h>
97 #import <WebCore/MIMETypeRegistry.h>
98 #import <WebCore/Page.h>
99 #import <WebCore/PlatformKeyboardEvent.h>
100 #import <WebCore/PlatformMouseEvent.h>
101 #import <WebCore/Range.h>
102 #import <WebCore/SelectionController.h>
103 #import <WebCore/SharedBuffer.h>
104 #import <WebCore/SimpleFontData.h>
105 #import <WebCore/Text.h>
106 #import <WebCore/WebCoreObjCExtras.h>
107 #import <WebCore/WebCoreTextRenderer.h>
108 #import <WebCore/markup.h>
109 #import <WebKit/DOM.h>
110 #import <WebKit/DOMExtensions.h>
111 #import <WebKit/DOMPrivate.h>
112 #import <WebKitSystemInterface.h>
114 #import <runtime/InitializeThreading.h>
116 #if USE(ACCELERATED_COMPOSITING)
117 #import <QuartzCore/QuartzCore.h>
120 using namespace WebCore;
121 using namespace HTMLNames;
124 @interface NSWindow (BorderViewAccess)
125 - (NSView*)_web_borderView;
128 @implementation NSWindow (BorderViewAccess)
129 - (NSView*)_web_borderView
135 static IMP oldSetCursorIMP = NULL;
137 #ifdef BUILDING_ON_TIGER
138 static IMP oldResetCursorRectsIMP = NULL;
139 static BOOL canSetCursor = YES;
141 static void resetCursorRects(NSWindow* self, SEL cmd)
143 NSPoint point = [self mouseLocationOutsideOfEventStream];
144 NSView* view = [[self _web_borderView] hitTest:point];
145 if ([view isKindOfClass:[WebHTMLView class]]) {
146 WebHTMLView *htmlView = (WebHTMLView*)view;
147 NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
148 NSDictionary *dict = [htmlView elementAtPoint:localPoint allowShadowContent:NO];
149 DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
150 if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
151 ![element isKindOfClass:[DOMHTMLEmbedElement class]])
154 oldResetCursorRectsIMP(self, cmd);
158 static void setCursor(NSCursor* self, SEL cmd)
161 oldSetCursorIMP(self, cmd);
164 static void setCursor(NSWindow* self, SEL cmd, NSPoint point)
166 NSView* view = [[self _web_borderView] hitTest:point];
167 if ([view isKindOfClass:[WebHTMLView class]]) {
168 WebHTMLView *htmlView = (WebHTMLView*)view;
169 NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
170 NSDictionary *dict = [htmlView elementAtPoint:localPoint allowShadowContent:NO];
171 DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
172 if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
173 ![element isKindOfClass:[DOMHTMLEmbedElement class]])
176 oldSetCursorIMP(self, cmd, point);
180 #if USE(ACCELERATED_COMPOSITING)
181 @interface WebLayerHostingView : NSView
184 @implementation WebLayerHostingView
185 // Empty NSViews intercept rightMouseDown: to do context menu handling, but we need the WebLayerHostingView to
186 // let right mouse clicks through.
187 - (void)rightMouseDown:(NSEvent *)theEvent
189 [[self nextResponder] performSelector:_cmd withObject:theEvent];
192 #endif // USE(ACCELERATED_COMPOSITING)
196 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
198 extern NSString *NSMarkedClauseSegmentAttributeName;
199 extern NSString *NSTextInputReplacementRangeAttributeName;
202 @interface NSView (WebNSViewDetails)
203 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
204 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
205 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView;
206 - (NSRect)_dirtyRect;
207 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
208 - (void)_propagateDirtyRectsToOpaqueAncestors;
209 - (void)_windowChangedKeyState;
212 @interface NSApplication (WebNSApplicationDetails)
213 - (void)speakString:(NSString *)string;
216 @interface NSWindow (WebNSWindowDetails)
217 - (id)_newFirstResponderAfterResigning;
218 - (void)_setForceActiveControls:(BOOL)flag;
221 @interface NSAttributedString (WebNSAttributedStringDetails)
222 - (id)_initWithDOMRange:(DOMRange *)range;
223 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
226 @interface NSSpellChecker (WebNSSpellCheckerDetails)
227 - (void)learnWord:(NSString *)word;
230 // By imaging to a width a little wider than the available pixels,
231 // thin pages will be scaled down a little, matching the way they
232 // print in IE and Camino. This lets them use fewer sheets than they
233 // would otherwise, which is presumably why other browsers do this.
234 // Wide pages will be scaled down more than this.
235 #define PrintingMinimumShrinkFactor 1.25f
237 // This number determines how small we are willing to reduce the page content
238 // in order to accommodate the widest line. If the page would have to be
239 // reduced smaller to make the widest line fit, we just clip instead (this
240 // behavior matches MacIE and Mozilla, at least)
241 #define PrintingMaximumShrinkFactor 2.0f
243 // This number determines how short the last printed page of a multi-page print session
244 // can be before we try to shrink the scale in order to reduce the number of pages, and
245 // thus eliminate the orphan.
246 #define LastPrintedPageOrphanRatio 0.1f
248 // This number determines the amount the scale factor is adjusted to try to eliminate orphans.
249 // It has no direct mathematical relationship to LastPrintedPageOrphanRatio, due to variable
250 // numbers of pages, logic to avoid breaking elements, and CSS-supplied hard page breaks.
251 #define PrintingOrphanShrinkAdjustment 1.1f
253 #define AUTOSCROLL_INTERVAL 0.1f
255 #define DRAG_LABEL_BORDER_X 4.0f
256 //Keep border_y in synch with DragController::LinkDragBorderInset
257 #define DRAG_LABEL_BORDER_Y 2.0f
258 #define DRAG_LABEL_RADIUS 5.0f
259 #define DRAG_LABEL_BORDER_Y_OFFSET 2.0f
261 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120.0f
262 #define MAX_DRAG_LABEL_WIDTH 320.0f
264 #define DRAG_LINK_LABEL_FONT_SIZE 11.0f
265 #define DRAG_LINK_URL_FONT_SIZE 10.0f
267 // Any non-zero value will do, but using something recognizable might help us debug some day.
268 #define TRACKING_RECT_TAG 0xBADFACE
270 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
271 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
273 #define STANDARD_WEIGHT 5
274 #define MIN_BOLD_WEIGHT 7
275 #define STANDARD_BOLD_WEIGHT 9
278 #define WebDataProtocolScheme @"webkit-fake-url"
280 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
281 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
282 @interface WebCoreScrollView : NSScrollView
285 @implementation WebCoreScrollView
288 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
289 static BOOL forceNSViewHitTest;
291 // 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])
292 static BOOL forceWebHTMLViewHitTest;
294 static WebHTMLView *lastHitView;
296 // We need this to be able to safely reference the CachedImage for the promised drag data
297 static CachedResourceClient* promisedDataClient()
299 static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
300 return staticCachedResourceClient;
303 @interface WebHTMLView (WebHTMLViewFileInternal)
304 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
305 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
306 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
307 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
308 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
309 - (void)_removeMouseMovedObserverUnconditionally;
310 - (void)_removeSuperviewObservers;
311 - (void)_removeWindowObservers;
312 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
313 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
314 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
315 - (float)_calculatePrintHeight;
316 - (DOMRange *)_selectedRange;
317 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
318 - (NSView *)_hitViewForEvent:(NSEvent *)event;
319 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
320 - (DOMRange *)_documentRange;
321 - (void)_setMouseDownEvent:(NSEvent *)event;
322 - (WebHTMLView *)_topHTMLView;
323 - (BOOL)_isTopHTMLView;
324 - (void)_web_setPrintingModeRecursive;
325 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
326 - (void)_web_clearPrintingModeRecursive;
329 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
330 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize;
333 @class NSTextInputContext;
334 @interface NSResponder (AppKitDetails)
335 - (NSTextInputContext *)inputContext;
338 @interface NSObject (NSTextInputContextDetails)
339 - (BOOL)wantsToHandleMouseEvents;
340 - (BOOL)handleMouseEvent:(NSEvent *)event;
343 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
344 - (void)_updateSelectionForInputManager;
347 @interface WebHTMLView (WebEditingStyleSupport)
348 - (DOMCSSStyleDeclaration *)_emptyStyle;
349 - (NSString *)_colorAsString:(NSColor *)color;
352 @interface NSView (WebHTMLViewFileInternal)
353 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
356 @interface NSMutableDictionary (WebHTMLViewFileInternal)
357 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
360 // Handles the complete: text command
361 @interface WebTextCompleteController : NSObject <NSTableViewDelegate, NSTableViewDataSource> {
364 NSWindow *_popupWindow;
365 NSTableView *_tableView;
366 NSArray *_completions;
367 NSString *_originalString;
370 - (id)initWithHTMLView:(WebHTMLView *)view;
371 - (void)doCompletion;
372 - (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft;
373 - (BOOL)popupWindowIsOpen;
374 - (BOOL)filterKeyDown:(NSEvent *)event;
375 - (void)_reflectSelection;
378 struct WebHTMLViewInterpretKeyEventsParameters {
379 KeyboardEvent* event;
380 BOOL eventWasHandled;
381 BOOL shouldSaveCommand;
382 // The Input Method may consume an event and not tell us, in
383 // which case we should not bubble the event up the DOM
387 @interface WebHTMLViewPrivate : NSObject {
391 BOOL needsToApplyStyles;
392 BOOL ignoringMouseDraggedEvents;
394 BOOL avoidingPrintOrphan;
395 BOOL observingMouseMovedNotifications;
396 BOOL observingSuperviewNotifications;
397 BOOL observingWindowNotifications;
398 BOOL resigningFirstResponder;
401 BOOL subviewsSetAside;
403 #if USE(ACCELERATED_COMPOSITING)
404 NSView *layerHostingView;
407 NSEvent *mouseDownEvent; // Kept after handling the event.
408 BOOL handlingMouseDownEvent;
409 NSEvent *keyDownEvent; // Kept after handling the event.
411 NSSize lastLayoutSize;
413 NSPoint lastScrollPosition;
415 WebPluginController *pluginController;
418 NSToolTipTag lastToolTipTag;
419 id trackingRectOwner;
420 void *trackingRectUserData;
422 NSTimer *autoscrollTimer;
423 NSEvent *autoscrollTriggerEvent;
427 NSMutableDictionary *highlighters;
429 #ifdef BUILDING_ON_TIGER
430 BOOL nextResponderDisabledOnce;
433 WebTextCompleteController *compController;
435 BOOL transparentBackground;
437 WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters;
440 WebDataSource *dataSource;
441 WebCore::CachedImage* promisedDragTIFFDataSource;
443 CFRunLoopTimerRef updateFocusedAndActiveStateTimer;
444 CFRunLoopTimerRef updateMouseoverTimer;
446 SEL selectorForDoCommandBySelector;
449 BOOL enumeratingSubviews;
455 static NSCellStateValue kit(TriState state)
465 ASSERT_NOT_REACHED();
469 @implementation WebHTMLViewPrivate
473 JSC::initializeThreading();
474 #ifndef BUILDING_ON_TIGER
475 WebCoreObjCFinalizeOnMainThread(self);
478 if (!oldSetCursorIMP) {
479 #ifdef BUILDING_ON_TIGER
480 Method setCursorMethod = class_getInstanceMethod([NSCursor class], @selector(set));
482 Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
484 ASSERT(setCursorMethod);
486 oldSetCursorIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
487 ASSERT(oldSetCursorIMP);
490 #ifdef BUILDING_ON_TIGER
491 if (!oldResetCursorRectsIMP) {
492 Method resetCursorRectsMethod = class_getInstanceMethod([NSWindow class], @selector(resetCursorRects));
493 ASSERT(resetCursorRectsMethod);
494 oldResetCursorRectsIMP = method_setImplementation(resetCursorRectsMethod, (IMP)resetCursorRects);
495 ASSERT(oldResetCursorRectsIMP);
503 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self))
506 ASSERT(!autoscrollTimer);
507 ASSERT(!autoscrollTriggerEvent);
508 ASSERT(!updateFocusedAndActiveStateTimer);
509 ASSERT(!updateMouseoverTimer);
511 [mouseDownEvent release];
512 [keyDownEvent release];
513 [pluginController release];
515 [compController release];
516 [dataSource release];
517 [highlighters release];
518 if (promisedDragTIFFDataSource)
519 promisedDragTIFFDataSource->removeClient(promisedDataClient());
526 ASSERT_MAIN_THREAD();
528 if (promisedDragTIFFDataSource)
529 promisedDragTIFFDataSource->removeClient(promisedDataClient());
536 [mouseDownEvent release];
537 [keyDownEvent release];
538 [pluginController release];
540 [compController release];
541 [dataSource release];
542 [highlighters release];
543 if (promisedDragTIFFDataSource)
544 promisedDragTIFFDataSource->removeClient(promisedDataClient());
546 mouseDownEvent = nil;
548 pluginController = nil;
550 compController = nil;
553 promisedDragTIFFDataSource = 0;
555 #if USE(ACCELERATED_COMPOSITING)
556 layerHostingView = nil;
562 @implementation WebHTMLView (WebHTMLViewFileInternal)
564 - (DOMRange *)_documentRange
566 return [[[self _frame] DOMDocument] _documentRange];
569 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
571 NSEnumerator *enumerator = [paths objectEnumerator];
574 while ((path = [enumerator nextObject]) != nil) {
575 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
576 if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
583 - (WebDataSource *)_dataSource
585 return _private->dataSource;
588 - (WebView *)_webView
590 return [_private->dataSource _webView];
593 - (WebFrameView *)_frameView
595 return [[_private->dataSource webFrame] frameView];
598 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
600 DOMDocumentFragment *fragment;
601 NSEnumerator *enumerator = [paths objectEnumerator];
602 NSMutableArray *domNodes = [[NSMutableArray alloc] init];
605 while ((path = [enumerator nextObject]) != nil) {
606 // Non-image file types; _web_userVisibleString is appropriate here because this will
607 // be pasted as visible text.
608 NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
609 [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
612 fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes];
616 return [fragment firstChild] != nil ? fragment : nil;
619 + (NSArray *)_excludedElementsForAttributedStringConversion
621 static NSArray *elements = nil;
622 if (elements == nil) {
623 elements = [[NSArray alloc] initWithObjects:
624 // Omit style since we want style to be inline so the fragment can be easily inserted.
626 // Omit xml so the result is not XHTML.
628 // Omit tags that will get stripped when converted to a fragment anyway.
629 @"doctype", @"html", @"head", @"body",
630 // Omit deprecated tags.
631 @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
632 // Omit object so no file attachments are part of the fragment.
639 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
641 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
642 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
644 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
645 CFRelease(UUIDString);
650 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
651 inContext:(DOMRange *)context
652 allowPlainText:(BOOL)allowPlainText
654 NSArray *types = [pasteboard types];
655 DOMDocumentFragment *fragment = nil;
657 if ([types containsObject:WebArchivePboardType] &&
658 (fragment = [self _documentFragmentFromPasteboard:pasteboard
659 forType:WebArchivePboardType
664 if ([types containsObject:NSFilenamesPboardType] &&
665 (fragment = [self _documentFragmentFromPasteboard:pasteboard
666 forType:NSFilenamesPboardType
671 if ([types containsObject:NSHTMLPboardType] &&
672 (fragment = [self _documentFragmentFromPasteboard:pasteboard
673 forType:NSHTMLPboardType
678 if ([types containsObject:NSRTFPboardType] &&
679 (fragment = [self _documentFragmentFromPasteboard:pasteboard
680 forType:NSRTFPboardType
685 if ([types containsObject:NSRTFDPboardType] &&
686 (fragment = [self _documentFragmentFromPasteboard:pasteboard
687 forType:NSRTFDPboardType
692 if ([types containsObject:NSTIFFPboardType] &&
693 (fragment = [self _documentFragmentFromPasteboard:pasteboard
694 forType:NSTIFFPboardType
699 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
700 if ([types containsObject:NSPICTPboardType] &&
701 (fragment = [self _documentFragmentFromPasteboard:pasteboard
702 forType:NSPICTPboardType
708 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
709 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
710 if ([types containsObject:(NSString*)kUTTypePNG] &&
711 (fragment = [self _documentFragmentFromPasteboard:pasteboard
712 forType:(NSString*)kUTTypePNG
717 if ([types containsObject:NSURLPboardType] &&
718 (fragment = [self _documentFragmentFromPasteboard:pasteboard
719 forType:NSURLPboardType
724 if (allowPlainText && [types containsObject:NSStringPboardType] &&
725 (fragment = [self _documentFragmentFromPasteboard:pasteboard
726 forType:NSStringPboardType
735 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
737 NSArray *types = [pasteboard types];
739 if ([types containsObject:NSStringPboardType])
740 return [pasteboard stringForType:NSStringPboardType];
742 NSAttributedString *attributedString = nil;
745 if ([types containsObject:NSRTFDPboardType])
746 attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
747 if (attributedString == nil && [types containsObject:NSRTFPboardType])
748 attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
749 if (attributedString != nil) {
750 string = [[attributedString string] copy];
751 [attributedString release];
752 return [string autorelease];
755 if ([types containsObject:NSFilenamesPboardType]) {
756 string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
763 if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
764 string = [URL _web_userVisibleString];
765 if ([string length] > 0)
772 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
774 DOMRange *range = [self _selectedRange];
775 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard
776 inContext:range allowPlainText:allowPlainText];
777 WebFrame *frame = [self _frame];
778 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[self _selectedRange] givenAction:WebViewInsertActionPasted]) {
779 [frame _replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:NO];
783 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
785 NSString *text = [self _plainTextFromPasteboard:pasteboard];
786 if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
787 [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
790 - (void)_removeMouseMovedObserverUnconditionally
792 if (!_private || !_private->observingMouseMovedNotifications)
795 [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil];
796 _private->observingMouseMovedNotifications = false;
799 - (void)_removeSuperviewObservers
801 if (!_private || !_private->observingSuperviewNotifications)
804 NSView *superview = [self superview];
805 if (!superview || ![self window])
808 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
809 [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview];
810 [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview];
812 _private->observingSuperviewNotifications = false;
815 - (void)_removeWindowObservers
817 if (!_private->observingWindowNotifications)
820 NSWindow *window = [self window];
824 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
825 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
826 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
827 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window];
828 [notificationCenter removeObserver:self name:WKWindowWillOrderOnScreenNotification() object:window];
830 _private->observingWindowNotifications = false;
833 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
835 WebView *webView = [self _webView];
836 DOMNode *child = [fragment firstChild];
837 if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
838 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
839 return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
842 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
844 WebView *webView = [self _webView];
845 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
848 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
850 return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
853 // Calculate the vertical size of the view that fits on a single page
854 - (float)_calculatePrintHeight
856 // Obtain the print info object for the current operation
857 NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
859 // Calculate the page height in points
860 NSSize paperSize = [pi paperSize];
861 return paperSize.height - [pi topMargin] - [pi bottomMargin];
864 - (DOMRange *)_selectedRange
866 Frame* coreFrame = core([self _frame]);
867 return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil;
870 - (BOOL)_shouldDeleteRange:(DOMRange *)range
872 Frame* coreFrame = core([self _frame]);
873 return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
876 - (NSView *)_hitViewForEvent:(NSEvent *)event
878 // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.
879 // Callers of this method, however, want to query the deepest view instead.
880 forceNSViewHitTest = YES;
881 NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
882 forceNSViewHitTest = NO;
886 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
888 // Put HTML on the pasteboard.
889 if ([types containsObject:WebArchivePboardType]) {
890 if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) {
891 if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0)
892 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType];
896 // Put the attributed string on the pasteboard (RTF/RTFD format).
897 if ([types containsObject:NSRTFDPboardType]) {
898 if (attributedString == nil) {
899 attributedString = [self selectedAttributedString];
901 NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
902 [pasteboard setData:RTFDData forType:NSRTFDPboardType];
904 if ([types containsObject:NSRTFPboardType]) {
905 if (attributedString == nil) {
906 attributedString = [self selectedAttributedString];
908 if ([attributedString containsAttachments]) {
909 attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
911 NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
912 [pasteboard setData:RTFData forType:NSRTFPboardType];
915 // Put plain string on the pasteboard.
916 if ([types containsObject:NSStringPboardType]) {
917 // Map to a plain old space because this is better for source code, other browsers do it,
918 // and because HTML forces you to do this any time you want two spaces in a row.
919 NSMutableString *s = [[self selectedString] mutableCopy];
920 const unichar NonBreakingSpaceCharacter = 0xA0;
921 NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
922 [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
923 [pasteboard setString:s forType:NSStringPboardType];
927 if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
928 [pasteboard setData:nil forType:WebSmartPastePboardType];
932 - (void)_setMouseDownEvent:(NSEvent *)event
934 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
936 if (event == _private->mouseDownEvent)
940 [_private->mouseDownEvent release];
941 _private->mouseDownEvent = event;
944 - (void)_cancelUpdateFocusedAndActiveStateTimer
946 if (_private->updateFocusedAndActiveStateTimer) {
947 CFRunLoopTimerInvalidate(_private->updateFocusedAndActiveStateTimer);
948 CFRelease(_private->updateFocusedAndActiveStateTimer);
949 _private->updateFocusedAndActiveStateTimer = NULL;
953 - (void)_cancelUpdateMouseoverTimer
955 if (_private->updateMouseoverTimer) {
956 CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
957 CFRelease(_private->updateMouseoverTimer);
958 _private->updateMouseoverTimer = NULL;
962 - (WebHTMLView *)_topHTMLView
964 // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
965 WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
967 ASSERT([view isKindOfClass:[WebHTMLView class]]);
971 - (BOOL)_isTopHTMLView
973 // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
974 return self == [self _topHTMLView];
977 - (void)_web_setPrintingModeRecursive
979 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
982 _private->enumeratingSubviews = YES;
985 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
987 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
989 unsigned count = [descendantWebHTMLViews count];
990 for (unsigned i = 0; i < count; ++i)
991 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
993 [descendantWebHTMLViews release];
996 _private->enumeratingSubviews = NO;
1000 - (void)_web_clearPrintingModeRecursive
1002 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
1005 _private->enumeratingSubviews = YES;
1008 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1010 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1012 unsigned count = [descendantWebHTMLViews count];
1013 for (unsigned i = 0; i < count; ++i)
1014 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
1016 [descendantWebHTMLViews release];
1019 _private->enumeratingSubviews = NO;
1023 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize
1025 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
1028 _private->enumeratingSubviews = YES;
1031 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1033 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1035 unsigned count = [descendantWebHTMLViews count];
1036 for (unsigned i = 0; i < count; ++i)
1037 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
1039 [descendantWebHTMLViews release];
1042 _private->enumeratingSubviews = NO;
1048 @implementation WebHTMLView (WebPrivate)
1050 + (NSArray *)supportedMIMETypes
1052 return [WebHTMLRepresentation supportedMIMETypes];
1055 + (NSArray *)supportedImageMIMETypes
1057 return [WebHTMLRepresentation supportedImageMIMETypes];
1060 + (NSArray *)supportedNonImageMIMETypes
1062 return [WebHTMLRepresentation supportedNonImageMIMETypes];
1065 + (NSArray *)unsupportedTextMIMETypes
1067 return [NSArray arrayWithObjects:
1068 @"text/calendar", // iCal
1070 @"text/x-vcalendar",
1072 @"text/vcard", // vCard
1075 @"text/ldif", // Netscape Address Book
1076 @"text/qif", // Quicken
1078 @"text/x-csv", // CSV (for Address Book and Microsoft Outlook)
1079 @"text/x-vcf", // vCard type used in Sun affinity app
1080 @"text/rtf", // Rich Text Format
1084 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
1086 // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged
1087 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1088 location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
1089 modifierFlags:[flagsChangedEvent modifierFlags]
1090 timestamp:[flagsChangedEvent timestamp]
1091 windowNumber:[flagsChangedEvent windowNumber]
1092 context:[flagsChangedEvent context]
1093 eventNumber:0 clickCount:0 pressure:0];
1095 // Pretend it's a mouse move.
1096 [[NSNotificationCenter defaultCenter]
1097 postNotificationName:WKMouseMovedNotification() object:self
1098 userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
1103 // This method exists to maintain compatibility with Leopard's Dictionary.app, since it
1104 // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:.
1105 // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160>
1106 return [self _frame];
1109 - (void)_updateMouseoverWithFakeEvent
1111 [self _cancelUpdateMouseoverTimer];
1113 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1114 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1115 modifierFlags:[[NSApp currentEvent] modifierFlags]
1116 timestamp:[NSDate timeIntervalSinceReferenceDate]
1117 windowNumber:[[self window] windowNumber]
1118 context:[[NSApp currentEvent] context]
1119 eventNumber:0 clickCount:0 pressure:0];
1121 [self _updateMouseoverWithEvent:fakeEvent];
1124 static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
1126 WebHTMLView *view = (WebHTMLView *)info;
1128 [view _updateMouseoverWithFakeEvent];
1131 - (void)_frameOrBoundsChanged
1133 if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
1134 [self setNeedsLayout:YES];
1135 [self setNeedsDisplay:YES];
1136 [_private->compController endRevertingChange:NO moveLeft:NO];
1139 NSPoint origin = [[self superview] bounds].origin;
1140 if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
1141 if (Frame* coreFrame = core([self _frame]))
1142 coreFrame->eventHandler()->sendScrollEvent();
1143 [_private->compController endRevertingChange:NO moveLeft:NO];
1145 WebView *webView = [self _webView];
1146 [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
1148 _private->lastScrollPosition = origin;
1150 if ([self window] && !_private->closed && !_private->updateMouseoverTimer) {
1151 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
1153 // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading
1154 // and scrolling rapidly back to back.
1155 _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0,
1156 _updateMouseoverTimerCallback, &context);
1157 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode);
1161 - (void)_setAsideSubviews
1163 ASSERT(!_private->subviewsSetAside);
1164 ASSERT(_private->savedSubviews == nil);
1165 _private->savedSubviews = _subviews;
1166 #if USE(ACCELERATED_COMPOSITING)
1167 // We need to keep the layer-hosting view in the subviews, otherwise the layers flash.
1168 if (_private->layerHostingView) {
1169 NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil];
1170 _subviews = newSubviews;
1176 _private->subviewsSetAside = YES;
1179 - (void)_restoreSubviews
1181 ASSERT(_private->subviewsSetAside);
1182 #if USE(ACCELERATED_COMPOSITING)
1183 if (_private->layerHostingView) {
1184 [_subviews release];
1185 _subviews = _private->savedSubviews;
1187 ASSERT(_subviews == nil);
1188 _subviews = _private->savedSubviews;
1191 ASSERT(_subviews == nil);
1192 _subviews = _private->savedSubviews;
1194 _private->savedSubviews = nil;
1195 _private->subviewsSetAside = NO;
1200 - (void)didAddSubview:(NSView *)subview
1202 if (_private->enumeratingSubviews)
1203 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]));
1206 - (void)willRemoveSubview:(NSView *)subview
1208 // Have to null-check _private, since this can be called via -dealloc when
1209 // cleaning up the the layerHostingView.
1210 if (_private && _private->enumeratingSubviews)
1211 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]));
1216 #ifdef BUILDING_ON_TIGER
1218 // This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
1219 // That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty
1220 // before doing it. As a compromise, when we're opaque we do the layout only when actually asked to
1221 // draw, but when we're transparent we do the layout at this stage so views behind us know that they
1222 // need to be redrawn (in case the layout causes some things to get dirtied).
1223 - (void)_propagateDirtyRectsToOpaqueAncestors
1225 if (![[self _webView] drawsBackground])
1226 [self _web_layoutIfNeededRecursive];
1227 [super _propagateDirtyRectsToOpaqueAncestors];
1232 - (void)viewWillDraw
1234 // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1235 // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1236 // once the FIXME in _isTopHTMLView is fixed.
1237 if (_private->dataSource && [self _isTopHTMLView])
1238 [self _web_layoutIfNeededRecursive];
1239 [super viewWillDraw];
1244 // Don't let AppKit even draw subviews. We take care of that.
1245 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1247 // This helps when we print as part of a larger print process.
1248 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1249 BOOL wasInPrintingMode = _private->printing;
1250 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1252 if (!wasInPrintingMode)
1253 [self _web_setPrintingModeRecursive];
1254 #ifndef BUILDING_ON_TIGER
1256 [self _web_layoutIfNeededRecursive];
1258 } else if (wasInPrintingMode)
1259 [self _web_clearPrintingModeRecursive];
1261 #ifdef BUILDING_ON_TIGER
1263 // Because Tiger does not have viewWillDraw we need to do layout here.
1264 [self _web_layoutIfNeededRecursive];
1265 [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
1269 [self _setAsideSubviews];
1270 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1271 [self _restoreSubviews];
1273 if (wasInPrintingMode != isPrinting) {
1274 if (wasInPrintingMode)
1275 [self _web_setPrintingModeRecursive];
1277 [self _web_clearPrintingModeRecursive];
1281 // Don't let AppKit even draw subviews. We take care of that.
1282 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1284 BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1286 BOOL wasInPrintingMode = _private->printing;
1287 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1289 if (needToSetAsideSubviews) {
1290 // This helps when we print as part of a larger print process.
1291 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1293 if (!wasInPrintingMode)
1294 [self _web_setPrintingModeRecursive];
1295 #ifndef BUILDING_ON_TIGER
1297 [self _web_layoutIfNeededRecursive];
1299 } else if (wasInPrintingMode)
1300 [self _web_clearPrintingModeRecursive];
1302 #ifdef BUILDING_ON_TIGER
1304 // Because Tiger does not have viewWillDraw we need to do layout here.
1305 NSRect boundsBeforeLayout = [self bounds];
1306 if (!NSIsEmptyRect(visRect))
1307 [self _web_layoutIfNeededRecursive];
1309 // If layout changes the view's bounds, then we need to recompute the visRect.
1310 // That's because the visRect passed to us was based on the bounds at the time
1311 // we were called. This method is only displayed to draw "all", so it's safe
1312 // to just call visibleRect to compute the entire rectangle.
1313 if (!NSEqualRects(boundsBeforeLayout, [self bounds]))
1314 visRect = [self visibleRect];
1318 [self _setAsideSubviews];
1321 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1323 if (needToSetAsideSubviews) {
1324 if (wasInPrintingMode != isPrinting) {
1325 if (wasInPrintingMode)
1326 [self _web_setPrintingModeRecursive];
1328 [self _web_clearPrintingModeRecursive];
1331 [self _restoreSubviews];
1335 // Don't let AppKit even draw subviews. We take care of that.
1336 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView
1338 #ifdef BUILDING_ON_TIGER
1339 // Because Tiger does not have viewWillDraw we need to do layout here.
1340 [self _web_layoutIfNeededRecursive];
1343 [self _setAsideSubviews];
1344 [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView];
1345 [self _restoreSubviews];
1348 - (BOOL)_insideAnotherHTMLView
1350 return self != [self _topHTMLView];
1353 - (NSView *)hitTest:(NSPoint)point
1355 // WebHTMLView objects handle all events for objects inside them.
1356 // To get those events, we prevent hit testing from AppKit.
1358 // But there are three exceptions to this:
1359 // 1) For right mouse clicks and control clicks we don't yet have an implementation
1360 // that works for nested views, so we let the hit testing go through the
1361 // standard NSView code path (needs to be fixed, see bug 4361618).
1362 // 2) Java depends on doing a hit test inside it's mouse moved handling,
1363 // so we let the hit testing go through the standard NSView code path
1364 // when the current event is a mouse move (except when we are calling
1365 // from _updateMouseoverWithEvent, so we have to use a global,
1366 // forceWebHTMLViewHitTest, for that)
1367 // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1368 // both need to figure out which view to check with inside the WebHTMLView.
1369 // They use a global to change the behavior of hitTest: so they can get the
1370 // right view. The global is forceNSViewHitTest and the method they use to
1371 // do the hit testing is _hitViewForEvent:. (But this does not work correctly
1372 // when there is HTML overlapping the view, see bug 4361626)
1373 // 4) NSAccessibilityHitTest relies on this for checking the cursor position.
1374 // Our check for that is whether the event is NSFlagsChanged. This works
1375 // for VoiceOver's cntl-opt-f5 command (move focus to item under cursor)
1376 // and Dictionary's cmd-cntl-D (open dictionary popup for item under cursor).
1377 // This is of course a hack.
1379 BOOL captureHitsOnSubviews;
1380 if (forceNSViewHitTest)
1381 captureHitsOnSubviews = NO;
1382 else if (forceWebHTMLViewHitTest)
1383 captureHitsOnSubviews = YES;
1385 NSEvent *event = [[self window] currentEvent];
1386 captureHitsOnSubviews = !([event type] == NSMouseMoved
1387 || [event type] == NSRightMouseDown
1388 || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1389 || [event type] == NSFlagsChanged);
1392 if (!captureHitsOnSubviews)
1393 return [super hitTest:point];
1394 if ([[self superview] mouse:point inRect:[self frame]])
1399 - (void)_clearLastHitViewIfSelf
1401 if (lastHitView == self)
1405 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1407 ASSERT(_private->trackingRectOwner == nil);
1408 _private->trackingRectOwner = owner;
1409 _private->trackingRectUserData = data;
1410 return TRACKING_RECT_TAG;
1413 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1415 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1416 ASSERT(_private->trackingRectOwner == nil);
1417 _private->trackingRectOwner = owner;
1418 _private->trackingRectUserData = data;
1419 return TRACKING_RECT_TAG;
1422 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1425 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1426 ASSERT(_private->trackingRectOwner == nil);
1427 _private->trackingRectOwner = owner;
1428 _private->trackingRectUserData = userDataList[0];
1429 trackingNums[0] = TRACKING_RECT_TAG;
1432 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1437 if (_private && (tag == TRACKING_RECT_TAG)) {
1438 _private->trackingRectOwner = nil;
1442 if (_private && (tag == _private->lastToolTipTag)) {
1443 [super removeTrackingRect:tag];
1444 _private->lastToolTipTag = 0;
1448 // If any other tracking rect is being removed, we don't know how it was created
1449 // and it's possible there's a leak involved (see 3500217)
1450 ASSERT_NOT_REACHED();
1453 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1456 for (i = 0; i < count; ++i) {
1460 ASSERT(tag == TRACKING_RECT_TAG);
1461 if (_private != nil) {
1462 _private->trackingRectOwner = nil;
1467 - (void)_sendToolTipMouseExited
1469 // Nothing matters except window, trackingNumber, and userData.
1470 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1471 location:NSMakePoint(0, 0)
1474 windowNumber:[[self window] windowNumber]
1477 trackingNumber:TRACKING_RECT_TAG
1478 userData:_private->trackingRectUserData];
1479 [_private->trackingRectOwner mouseExited:fakeEvent];
1482 - (void)_sendToolTipMouseEntered
1484 // Nothing matters except window, trackingNumber, and userData.
1485 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1486 location:NSMakePoint(0, 0)
1489 windowNumber:[[self window] windowNumber]
1492 trackingNumber:TRACKING_RECT_TAG
1493 userData:_private->trackingRectUserData];
1494 [_private->trackingRectOwner mouseEntered:fakeEvent];
1497 - (void)_setToolTip:(NSString *)string
1499 NSString *toolTip = [string length] == 0 ? nil : string;
1500 NSString *oldToolTip = _private->toolTip;
1501 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1505 [self _sendToolTipMouseExited];
1506 [oldToolTip release];
1508 _private->toolTip = [toolTip copy];
1510 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1511 [self removeAllToolTips];
1512 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1513 _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1514 [self _sendToolTipMouseEntered];
1518 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1520 return [[_private->toolTip copy] autorelease];
1523 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1525 if (_private->closed)
1528 NSView *contentView = [[event window] contentView];
1529 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1531 forceWebHTMLViewHitTest = YES;
1532 NSView *hitView = [contentView hitTest:locationForHitTest];
1533 forceWebHTMLViewHitTest = NO;
1535 WebHTMLView *view = nil;
1536 if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
1537 view = (WebHTMLView *)hitView;
1542 if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1543 // If we are moving out of a view (or frame), let's pretend the mouse moved
1544 // all the way out of that view. But we have to account for scrolling, because
1545 // khtml doesn't understand our clipping.
1546 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1547 float yScroll = visibleRect.origin.y;
1548 float xScroll = visibleRect.origin.x;
1550 event = [NSEvent mouseEventWithType:NSMouseMoved
1551 location:NSMakePoint(-1 - xScroll, -1 - yScroll )
1552 modifierFlags:[[NSApp currentEvent] modifierFlags]
1553 timestamp:[NSDate timeIntervalSinceReferenceDate]
1554 windowNumber:[[view window] windowNumber]
1555 context:[[NSApp currentEvent] context]
1556 eventNumber:0 clickCount:0 pressure:0];
1557 if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1558 lastHitCoreFrame->eventHandler()->mouseMoved(event);
1564 if (Frame* coreFrame = core([view _frame]))
1565 coreFrame->eventHandler()->mouseMoved(event);
1571 // keep in sync with WebPasteboardHelper::insertablePasteboardTypes
1572 + (NSArray *)_insertablePasteboardTypes
1574 static NSArray *types = nil;
1576 types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType,
1577 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
1580 NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
1586 + (NSArray *)_selectionPasteboardTypes
1588 // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1589 return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1592 - (NSImage *)_dragImageForURL:(NSString*)urlString withLabel:(NSString*)label
1594 BOOL drawURLString = YES;
1595 BOOL clipURLString = NO, clipLabelString = NO;
1602 NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
1603 toHaveTrait:NSBoldFontMask];
1604 NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
1606 labelSize.width = [label _web_widthWithFont: labelFont];
1607 labelSize.height = [labelFont ascender] - [labelFont descender];
1608 if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
1609 labelSize.width = MAX_DRAG_LABEL_WIDTH;
1610 clipLabelString = YES;
1613 NSSize imageSize, urlStringSize;
1614 imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2.0f;
1615 imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2.0f;
1616 if (drawURLString) {
1617 urlStringSize.width = [urlString _web_widthWithFont: urlFont];
1618 urlStringSize.height = [urlFont ascender] - [urlFont descender];
1619 imageSize.height += urlStringSize.height;
1620 if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
1621 imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2.0f, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
1622 clipURLString = YES;
1624 imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2.0f, urlStringSize.width + DRAG_LABEL_BORDER_X * 2.0f);
1627 NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
1628 [dragImage lockFocus];
1630 [[NSColor colorWithDeviceRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
1632 // Drag a rectangle with rounded corners/
1633 NSBezierPath *path = [NSBezierPath bezierPath];
1634 [path appendBezierPathWithOvalInRect: NSMakeRect(0.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1635 [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1636 [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)];
1637 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1639 [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0.0f, imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height)];
1640 [path appendBezierPathWithRect: NSMakeRect(0.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1641 [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)];
1644 NSColor *topColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.75f];
1645 NSColor *bottomColor = [NSColor colorWithDeviceWhite:1.0f alpha:0.5f];
1646 if (drawURLString) {
1648 urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:urlFont];
1650 [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender])
1651 withTopColor:topColor bottomColor:bottomColor font:urlFont];
1654 if (clipLabelString)
1655 label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:labelFont];
1656 [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
1657 withTopColor:topColor bottomColor:bottomColor font:labelFont];
1659 [dragImage unlockFocus];
1664 - (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
1666 NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
1668 NSString *label = [element objectForKey: WebElementLinkLabelKey];
1669 NSString *urlString = [linkURL _web_userVisibleString];
1670 return [self _dragImageForURL:urlString withLabel:label];
1673 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1675 [self setPromisedDragTIFFDataSource:0];
1678 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1680 if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1681 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1682 [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1684 } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1685 if (Image* image = [self promisedDragTIFFDataSource]->image())
1686 [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1687 [self setPromisedDragTIFFDataSource:0];
1691 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
1693 [self autoscroll:event];
1694 [self _startAutoscrollTimer:event];
1697 - (WebPluginController *)_pluginController
1699 return _private->pluginController;
1702 - (void)_layoutForPrinting
1704 // Set printing mode temporarily so we can adjust the size of the view. This will allow
1705 // AppKit's pagination code to use the correct height for the page content. Leaving printing
1706 // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1707 // turn it off again after adjusting the size.
1708 [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1709 [self _web_clearPrintingModeRecursive];
1712 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1714 if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
1716 *beforeString = nil;
1722 [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
1725 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
1727 return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
1730 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1732 if (_private->autoscrollTimer == nil) {
1733 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1734 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1735 _private->autoscrollTriggerEvent = [triggerEvent retain];
1739 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1740 // We can't remove this yet because it's still in use by Mail.
1741 - (NSRect)_selectionRect
1743 return [self selectionRect];
1746 - (void)_stopAutoscrollTimer
1748 NSTimer *timer = _private->autoscrollTimer;
1749 _private->autoscrollTimer = nil;
1750 [_private->autoscrollTriggerEvent release];
1751 _private->autoscrollTriggerEvent = nil;
1758 // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1759 // a mouse up event.
1760 BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);
1762 [self _stopAutoscrollTimer];
1766 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1767 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1768 modifierFlags:[[NSApp currentEvent] modifierFlags]
1769 timestamp:[NSDate timeIntervalSinceReferenceDate]
1770 windowNumber:[[self window] windowNumber]
1771 context:[[NSApp currentEvent] context]
1772 eventNumber:0 clickCount:0 pressure:0];
1773 [self mouseDragged:fakeEvent];
1778 Frame* coreFrame = core([self _frame]);
1779 return coreFrame && coreFrame->editor()->canEdit();
1782 - (BOOL)_canEditRichly
1784 Frame* coreFrame = core([self _frame]);
1785 return coreFrame && coreFrame->editor()->canEditRichly();
1788 - (BOOL)_canAlterCurrentSelection
1790 return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1793 - (BOOL)_hasSelection
1795 Frame* coreFrame = core([self _frame]);
1796 return coreFrame && coreFrame->selection()->isRange();
1799 - (BOOL)_hasSelectionOrInsertionPoint
1801 Frame* coreFrame = core([self _frame]);
1802 return coreFrame && coreFrame->selection()->isCaretOrRange();
1805 - (BOOL)_hasInsertionPoint
1807 Frame* coreFrame = core([self _frame]);
1808 return coreFrame && coreFrame->selection()->isCaret();
1813 Frame* coreFrame = core([self _frame]);
1814 return coreFrame && coreFrame->selection()->isContentEditable();
1817 - (BOOL)_transparentBackground
1819 return _private->transparentBackground;
1822 - (void)_setTransparentBackground:(BOOL)f
1824 _private->transparentBackground = f;
1827 - (NSImage *)_selectionDraggingImage
1829 if ([self _hasSelection]) {
1830 NSImage *dragImage = core([self _frame])->selectionImage();
1831 [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1837 - (NSRect)_selectionDraggingRect
1839 // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1840 return [self selectionRect];
1843 - (DOMNode *)_insertOrderedList
1845 Frame* coreFrame = core([self _frame]);
1846 return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
1849 - (DOMNode *)_insertUnorderedList
1851 Frame* coreFrame = core([self _frame]);
1852 return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
1855 - (BOOL)_canIncreaseSelectionListLevel
1857 Frame* coreFrame = core([self _frame]);
1858 return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
1861 - (BOOL)_canDecreaseSelectionListLevel
1863 Frame* coreFrame = core([self _frame]);
1864 return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
1867 - (DOMNode *)_increaseSelectionListLevel
1869 Frame* coreFrame = core([self _frame]);
1870 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
1873 - (DOMNode *)_increaseSelectionListLevelOrdered
1875 Frame* coreFrame = core([self _frame]);
1876 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
1879 - (DOMNode *)_increaseSelectionListLevelUnordered
1881 Frame* coreFrame = core([self _frame]);
1882 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
1885 - (void)_decreaseSelectionListLevel
1887 Frame* coreFrame = core([self _frame]);
1889 coreFrame->editor()->decreaseSelectionListLevel();
1892 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1894 if (!_private->highlighters)
1895 _private->highlighters = [[NSMutableDictionary alloc] init];
1896 [_private->highlighters setObject:highlighter forKey:type];
1899 - (void)_removeHighlighterOfType:(NSString*)type
1901 [_private->highlighters removeObjectForKey:type];
1904 - (void)_updateFocusedAndActiveState
1906 [self _cancelUpdateFocusedAndActiveStateTimer];
1908 [[self _webView] _updateFocusedAndActiveStateForFrame:[self _frame]];
1911 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1913 ASSERT([self _hasSelection]);
1914 NSArray *types = [self pasteboardTypesForSelection];
1916 // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1917 NSAttributedString *attributedString = [self selectedAttributedString];
1918 NSMutableArray *mutableTypes = nil;
1919 if (![attributedString containsAttachments]) {
1920 mutableTypes = [types mutableCopy];
1921 [mutableTypes removeObject:NSRTFDPboardType];
1922 types = mutableTypes;
1925 [pasteboard declareTypes:types owner:[self _topHTMLView]];
1926 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1927 [mutableTypes release];
1932 // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing
1933 // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
1934 if (!_private || _private->closed)
1937 _private->closed = YES;
1939 [self _cancelUpdateMouseoverTimer];
1940 [self _cancelUpdateFocusedAndActiveStateTimer];
1941 [self _clearLastHitViewIfSelf];
1942 [self _removeMouseMovedObserverUnconditionally];
1943 [self _removeWindowObservers];
1944 [self _removeSuperviewObservers];
1945 [_private->pluginController destroyAllPlugins];
1946 [_private->pluginController setDataSource:nil];
1947 // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1948 [self removeAllToolTips];
1950 #if USE(ACCELERATED_COMPOSITING)
1951 if (_private->layerHostingView)
1952 [[self _webView] _stoppedAcceleratedCompositingForFrame:[self _frame]];
1957 Page* page = core([self _webView]);
1959 page->dragController()->setDraggingImageURL(KURL());
1962 - (BOOL)_hasHTMLDocument
1964 Frame* coreFrame = core([self _frame]);
1967 Document* document = coreFrame->document();
1968 return document && document->isHTMLDocument();
1971 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1972 forType:(NSString *)pboardType
1973 inContext:(DOMRange *)context
1974 subresources:(NSArray **)subresources
1976 if (pboardType == WebArchivePboardType) {
1977 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1979 *subresources = [archive subresources];
1980 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1984 if (pboardType == NSFilenamesPboardType)
1985 return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1987 if (pboardType == NSHTMLPboardType) {
1988 NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1989 // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1990 if ([HTMLString hasPrefix:@"Version:"]) {
1991 NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
1992 if (range.location != NSNotFound)
1993 HTMLString = [HTMLString substringFromIndex:range.location];
1995 if ([HTMLString length] == 0)
1998 return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
2001 // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
2002 // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
2003 // FIXME: Remove this once bug 5052369 is fixed.
2004 if ([self _hasHTMLDocument] && pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType) {
2005 NSAttributedString *string = nil;
2006 if (pboardType == NSRTFDPboardType)
2007 string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
2009 string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
2013 NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
2014 [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
2015 self, @"WebResourceHandler", nil];
2018 BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
2019 if (!wasDeferringCallbacks)
2020 [[self _webView] setDefersCallbacks:YES];
2022 DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length])
2023 document:[[self _frame] DOMDocument]
2024 documentAttributes:documentAttributes
2029 NSEnumerator *e = [s objectEnumerator];
2031 while ((r = [e nextObject]))
2032 [[self _dataSource] addSubresource:r];
2034 if (!wasDeferringCallbacks)
2035 [[self _webView] setDefersCallbacks:NO];
2037 [documentAttributes release];
2041 if (pboardType == NSTIFFPboardType) {
2042 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
2043 URL:uniqueURLWithRelativePart(@"image.tiff")
2044 MIMEType:@"image/tiff"
2045 textEncodingName:nil
2047 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2051 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
2052 if (pboardType == NSPICTPboardType) {
2053 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
2054 URL:uniqueURLWithRelativePart(@"image.pict")
2055 MIMEType:@"image/pict"
2056 textEncodingName:nil
2058 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2063 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
2064 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
2065 if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) {
2066 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG]
2067 URL:uniqueURLWithRelativePart(@"image.png")
2068 MIMEType:@"image/png"
2069 textEncodingName:nil
2071 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2075 if (pboardType == NSURLPboardType) {
2076 NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
2077 DOMDocument* document = [[self _frame] DOMDocument];
2081 DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
2082 NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose.
2083 if ([URLString length] == 0)
2085 NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping];
2086 DOMText *text = [document createTextNode:URLTitleString];
2087 [anchor setHref:URLString];
2088 [anchor appendChild:text];
2089 DOMDocumentFragment *fragment = [document createDocumentFragment];
2090 [fragment appendChild:anchor];
2093 if (pboardType == NSStringPboardType)
2094 return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get());
2098 #if ENABLE(NETSCAPE_PLUGIN_API)
2099 - (void)_pauseNullEventsForAllNetscapePlugins
2101 NSArray *subviews = [self subviews];
2102 unsigned int subviewCount = [subviews count];
2103 unsigned int subviewIndex;
2105 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2106 NSView *subview = [subviews objectAtIndex:subviewIndex];
2107 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2108 [(WebBaseNetscapePluginView *)subview stopTimers];
2113 #if ENABLE(NETSCAPE_PLUGIN_API)
2114 - (void)_resumeNullEventsForAllNetscapePlugins
2116 NSArray *subviews = [self subviews];
2117 unsigned int subviewCount = [subviews count];
2118 unsigned int subviewIndex;
2120 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2121 NSView *subview = [subviews objectAtIndex:subviewIndex];
2122 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2123 [(WebBaseNetscapePluginView *)subview restartTimers];
2128 - (BOOL)_isUsingAcceleratedCompositing
2130 #if USE(ACCELERATED_COMPOSITING)
2131 return _private->layerHostingView != nil;
2139 @implementation NSView (WebHTMLViewFileInternal)
2141 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
2143 unsigned count = [_subviews count];
2144 for (unsigned i = 0; i < count; ++i) {
2145 NSView *child = [_subviews objectAtIndex:i];
2146 if ([child isKindOfClass:[WebHTMLView class]])
2147 [array addObject:child];
2148 [child _web_addDescendantWebHTMLViewsToArray:array];
2154 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
2156 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
2158 if (object == nil) {
2159 [self removeObjectForKey:key];
2161 [self setObject:object forKey:key];
2167 @interface NSString (WebHTMLViewFileInternal)
2168 - (BOOL)matchesExtensionEquivalent:(NSString *)extension;
2171 @implementation NSString (WebHTMLViewFileInternal)
2173 - (BOOL)matchesExtensionEquivalent:(NSString *)extension
2175 if ([self hasSuffix:extension])
2177 else if ([extension isEqualToString:@"jpeg"] && [self hasSuffix:@"jpg"])
2184 #ifdef BUILDING_ON_TIGER
2186 // The following is a workaround for
2187 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
2188 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
2189 // Since the category will be searched before the real class, we'll prevent the flag from being
2190 // set on the tool tip panel.
2192 @interface NSToolTipPanel : NSPanel
2195 @interface NSToolTipPanel (WebHTMLViewFileInternal)
2198 @implementation NSToolTipPanel (WebHTMLViewFileInternal)
2200 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
2202 // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
2209 @interface NSArray (WebHTMLView)
2210 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
2213 @implementation WebHTMLView
2217 [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes]
2218 returnTypes:[[self class] _insertablePasteboardTypes]];
2219 JSC::initializeThreading();
2220 #ifndef BUILDING_ON_TIGER
2221 WebCoreObjCFinalizeOnMainThread(self);
2225 - (id)initWithFrame:(NSRect)frame
2227 self = [super initWithFrame:frame];
2231 [self setFocusRingType:NSFocusRingTypeNone];
2233 // Make all drawing go through us instead of subviews.
2234 [self _setDrawsOwnDescendants:YES];
2236 _private = [[WebHTMLViewPrivate alloc] init];
2238 _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2239 _private->needsLayout = YES;
2246 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self))
2249 // We can't assert that close has already been called because
2250 // this view can be removed from it's superview, even though
2251 // it could be needed later, so close if needed.
2260 ASSERT_MAIN_THREAD();
2261 // We can't assert that close has already been called because
2262 // this view can be removed from it's superview, even though
2263 // it could be needed later, so close if needed.
2268 // Returns YES if the delegate returns YES (so we should do no more work).
2269 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2271 BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2272 _private->selectorForDoCommandBySelector = 0;
2273 if (callerAlreadyCalledDelegate)
2275 WebView *webView = [self _webView];
2276 return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2279 static String commandNameForSelector(SEL selector)
2281 // Change a few command names into ones supported by WebCore::Editor.
2282 // If this list gets too long we might decide we need to use a hash table.
2283 if (selector == @selector(insertParagraphSeparator:) || selector == @selector(insertNewlineIgnoringFieldEditor:))
2284 return "InsertNewline";
2285 if (selector == @selector(insertTabIgnoringFieldEditor:))
2287 if (selector == @selector(pageDown:))
2288 return "MovePageDown";
2289 if (selector == @selector(pageDownAndModifySelection:))
2290 return "MovePageDownAndModifySelection";
2291 if (selector == @selector(pageUp:))
2292 return "MovePageUp";
2293 if (selector == @selector(pageUpAndModifySelection:))
2294 return "MovePageUpAndModifySelection";
2296 // Remove the trailing colon.
2297 const char* selectorName = sel_getName(selector);
2298 size_t selectorNameLength = strlen(selectorName);
2299 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2301 return String(selectorName, selectorNameLength - 1);
2304 - (Editor::Command)coreCommandBySelector:(SEL)selector
2306 Frame* coreFrame = core([self _frame]);
2308 return Editor::Command();
2309 return coreFrame->editor()->command(commandNameForSelector(selector));
2312 - (Editor::Command)coreCommandByName:(const char*)name
2314 Frame* coreFrame = core([self _frame]);
2316 return Editor::Command();
2317 return coreFrame->editor()->command(name);
2320 - (void)executeCoreCommandBySelector:(SEL)selector
2322 if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2324 [self coreCommandBySelector:selector].execute();
2327 - (void)executeCoreCommandByName:(const char*)name
2329 [self coreCommandByName:name].execute();
2332 // These commands are forwarded to the Editor object in WebCore.
2333 // Ideally we'd do this for all editing commands; more of the code
2334 // should be moved from here to there, and more commands should be
2335 // added to this list.
2337 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2338 // The functions are identical.
2340 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2342 WEBCORE_COMMAND(alignCenter)
2343 WEBCORE_COMMAND(alignJustified)
2344 WEBCORE_COMMAND(alignLeft)
2345 WEBCORE_COMMAND(alignRight)
2346 WEBCORE_COMMAND(copy)
2347 WEBCORE_COMMAND(cut)
2348 WEBCORE_COMMAND(delete)
2349 WEBCORE_COMMAND(deleteBackward)
2350 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2351 WEBCORE_COMMAND(deleteForward)
2352 WEBCORE_COMMAND(deleteToBeginningOfLine)
2353 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2354 WEBCORE_COMMAND(deleteToEndOfLine)
2355 WEBCORE_COMMAND(deleteToEndOfParagraph)
2356 WEBCORE_COMMAND(deleteToMark)
2357 WEBCORE_COMMAND(deleteWordBackward)
2358 WEBCORE_COMMAND(deleteWordForward)
2359 WEBCORE_COMMAND(ignoreSpelling)
2360 WEBCORE_COMMAND(indent)
2361 WEBCORE_COMMAND(insertBacktab)
2362 WEBCORE_COMMAND(insertLineBreak)
2363 WEBCORE_COMMAND(insertNewline)
2364 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2365 WEBCORE_COMMAND(insertParagraphSeparator)
2366 WEBCORE_COMMAND(insertTab)
2367 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2368 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
2369 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
2370 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
2371 WEBCORE_COMMAND(moveBackward)
2372 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2373 WEBCORE_COMMAND(moveDown)
2374 WEBCORE_COMMAND(moveDownAndModifySelection)
2375 WEBCORE_COMMAND(moveForward)
2376 WEBCORE_COMMAND(moveForwardAndModifySelection)
2377 WEBCORE_COMMAND(moveLeft)
2378 WEBCORE_COMMAND(moveLeftAndModifySelection)
2379 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2380 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2381 WEBCORE_COMMAND(moveRight)
2382 WEBCORE_COMMAND(moveRightAndModifySelection)
2383 WEBCORE_COMMAND(moveToBeginningOfDocument)
2384 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2385 WEBCORE_COMMAND(moveToBeginningOfLine)
2386 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2387 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2388 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2389 WEBCORE_COMMAND(moveToBeginningOfSentence)
2390 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2391 WEBCORE_COMMAND(moveToEndOfDocument)
2392 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2393 WEBCORE_COMMAND(moveToEndOfLine)
2394 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2395 WEBCORE_COMMAND(moveToEndOfParagraph)
2396 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2397 WEBCORE_COMMAND(moveToEndOfSentence)
2398 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2399 WEBCORE_COMMAND(moveToLeftEndOfLine)
2400 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
2401 WEBCORE_COMMAND(moveToRightEndOfLine)
2402 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
2403 WEBCORE_COMMAND(moveUp)
2404 WEBCORE_COMMAND(moveUpAndModifySelection)
2405 WEBCORE_COMMAND(moveWordBackward)
2406 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2407 WEBCORE_COMMAND(moveWordForward)
2408 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2409 WEBCORE_COMMAND(moveWordLeft)
2410 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2411 WEBCORE_COMMAND(moveWordRight)
2412 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2413 WEBCORE_COMMAND(outdent)
2414 WEBCORE_COMMAND(pageDown)
2415 WEBCORE_COMMAND(pageDownAndModifySelection)
2416 WEBCORE_COMMAND(pageUp)
2417 WEBCORE_COMMAND(pageUpAndModifySelection)
2418 WEBCORE_COMMAND(selectAll)
2419 WEBCORE_COMMAND(selectLine)
2420 WEBCORE_COMMAND(selectParagraph)
2421 WEBCORE_COMMAND(selectSentence)
2422 WEBCORE_COMMAND(selectToMark)
2423 WEBCORE_COMMAND(selectWord)
2424 WEBCORE_COMMAND(setMark)
2425 WEBCORE_COMMAND(subscript)
2426 WEBCORE_COMMAND(superscript)
2427 WEBCORE_COMMAND(swapWithMark)
2428 WEBCORE_COMMAND(transpose)
2429 WEBCORE_COMMAND(underline)
2430 WEBCORE_COMMAND(unscript)
2431 WEBCORE_COMMAND(yank)
2432 WEBCORE_COMMAND(yankAndSelect)
2434 #undef WEBCORE_COMMAND
2436 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2438 - (IBAction)takeFindStringFromSelection:(id)sender
2442 if (![self _hasSelection]) {
2447 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2450 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2452 [pasteboard declareTypes:types owner:[self _topHTMLView]];
2453 [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2457 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2459 Frame* coreFrame = core([self _frame]);
2462 if (coreFrame->selection()->isContentRichlyEditable())
2463 [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
2465 [self _pasteAsPlainTextWithPasteboard:pasteboard];
2469 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2471 BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]);
2472 BOOL isReturnTypeOK = !returnType || ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]);
2473 if (isSendTypeOK && isReturnTypeOK)
2475 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2478 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2479 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2480 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2481 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2482 // might be using the selector, and we don't want to break them.
2483 - (void)jumpToSelection:(id)sender
2487 if (Frame* coreFrame = core([self _frame]))
2488 coreFrame->revealSelection(ScrollAlignment::alignCenterAlways);
2491 - (NSCellStateValue)selectionHasStyle:(CSSStyleDeclaration*)style
2493 Frame* coreFrame = core([self _frame]);
2496 return kit(coreFrame->editor()->selectionHasStyle(style));
2499 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2501 SEL action = [item action];
2502 RefPtr<Frame> frame = core([self _frame]);
2507 if (Document* doc = frame->document()) {
2508 if (doc->isPluginDocument())
2510 if (doc->isImageDocument()) {
2511 if (action == @selector(copy:))
2512 return frame->loader()->isComplete();
2517 if (action == @selector(changeSpelling:)
2518 || action == @selector(_changeSpellingFromMenu:)
2519 || action == @selector(checkSpelling:)
2520 || action == @selector(complete:)
2521 || action == @selector(pasteFont:))
2522 return [self _canEdit];
2524 if (action == @selector(showGuessPanel:)) {
2525 #ifndef BUILDING_ON_TIGER
2526 // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2527 NSMenuItem *menuItem = (NSMenuItem *)item;
2528 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2529 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2530 [menuItem setTitle:panelShowing
2531 ? UI_STRING("Hide Spelling and Grammar", "menu item title")
2532 : UI_STRING("Show Spelling and Grammar", "menu item title")];
2535 return [self _canEdit];
2538 if (action == @selector(changeBaseWritingDirection:)
2539 || action == @selector(makeBaseWritingDirectionLeftToRight:)
2540 || action == @selector(makeBaseWritingDirectionRightToLeft:)) {
2541 NSWritingDirection writingDirection;
2543 if (action == @selector(changeBaseWritingDirection:)) {
2544 writingDirection = static_cast<NSWritingDirection>([item tag]);
2545 if (writingDirection == NSWritingDirectionNatural)
2547 } else if (action == @selector(makeBaseWritingDirectionLeftToRight:))
2548 writingDirection = NSWritingDirectionLeftToRight;
2550 writingDirection = NSWritingDirectionRightToLeft;
2552 NSMenuItem *menuItem = (NSMenuItem *)item;
2553 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2554 RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create();
2556 style->setProperty("direction", writingDirection == NSWritingDirectionLeftToRight ? "LTR" : "RTL", ec);
2557 [menuItem setState:frame->editor()->selectionHasStyle(style.get())];
2559 return [self _canEdit];
2562 if (action == @selector(makeBaseWritingDirectionNatural:)) {
2563 NSMenuItem *menuItem = (NSMenuItem *)item;
2564 if ([menuItem isKindOfClass:[NSMenuItem class]])
2565 [menuItem setState:NSOffState];
2569 if (action == @selector(toggleBaseWritingDirection:)) {
2570 NSMenuItem *menuItem = (NSMenuItem *)item;
2571 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2572 RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create();
2574 style->setProperty("direction", "RTL", ec);
2575 // Take control of the title of the menu item instead of just checking/unchecking it because
2576 // a check would be ambiguous.
2577 [menuItem setTitle:frame->editor()->selectionHasStyle(style.get())
2578 ? UI_STRING("Left to Right", "Left to Right context menu item")
2579 : UI_STRING("Right to Left", "Right to Left context menu item")];
2581 return [self _canEdit];
2584 if (action == @selector(changeAttributes:)
2585 || action == @selector(changeColor:)
2586 || action == @selector(changeFont:))
2587 return [self _canEditRichly];
2589 if (action == @selector(capitalizeWord:)
2590 || action == @selector(lowercaseWord:)
2591 || action == @selector(uppercaseWord:))
2592 return [self _hasSelection] && [self _isEditable];
2594 if (action == @selector(centerSelectionInVisibleArea:)
2595 || action == @selector(jumpToSelection:)
2596 || action == @selector(copyFont:))
2597 return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2599 if (action == @selector(changeDocumentBackgroundColor:))
2600 return [[self _webView] isEditable] && [self _canEditRichly];
2602 if (action == @selector(_ignoreSpellingFromMenu:)
2603 || action == @selector(_learnSpellingFromMenu:)
2604 || action == @selector(takeFindStringFromSelection:))
2605 return [self _hasSelection];
2607 if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2608 return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste());
2610 if (action == @selector(pasteAsRichText:))
2611 return frame && (frame->editor()->canDHTMLPaste()
2612 || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable()));
2614 if (action == @selector(performFindPanelAction:))
2617 if (action == @selector(_lookUpInDictionaryFromMenu:))
2618 return [self _hasSelection];
2620 #ifndef BUILDING_ON_TIGER
2621 if (action == @selector(toggleGrammarChecking:)) {
2622 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate
2623 // the selector here because we implement it here, and we must implement it here because the AppKit
2624 // code checks the first responder.
2625 NSMenuItem *menuItem = (NSMenuItem *)item;
2626 if ([menuItem isKindOfClass:[NSMenuItem class]])
2627 [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2632 Editor::Command command = [self coreCommandBySelector:action];
2633 if (command.isSupported()) {
2634 NSMenuItem *menuItem = (NSMenuItem *)item;
2635 if ([menuItem isKindOfClass:[NSMenuItem class]])
2636 [menuItem setState:kit(command.state())];
2637 return command.isEnabled();
2643 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2645 // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
2646 // assumes the WebVIew is non-nil.
2647 if (![self _webView])
2649 BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2650 return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2653 - (BOOL)acceptsFirstResponder
2655 // Don't accept first responder when we first click on this view.
2656 // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2657 // Do accept first responder at any other time, for example from keyboard events,
2658 // or from calls back from WebCore once we begin mouse-down event handling.
2659 NSEvent *event = [NSApp currentEvent];
2660 if ([event type] == NSLeftMouseDown
2661 && !_private->handlingMouseDownEvent
2662 && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2668 - (BOOL)maintainsInactiveSelection
2670 // This method helps to determine whether the WebHTMLView should maintain
2671 // an inactive selection when it's not first responder.
2672 // Traditionally, these views have not maintained such selections,
2673 // clearing them when the view was not first responder. However,
2674 // to fix bugs like this one:
2675 // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even
2676 // when they're not firstResponder"
2677 // it was decided to add a switch to act more like an NSTextView.
2679 if ([[self _webView] maintainsInactiveSelection])
2682 // Predict the case where we are losing first responder status only to
2683 // gain it back again. Want to keep the selection in that case.
2684 id nextResponder = [[self window] _newFirstResponderAfterResigning];
2685 if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2686 id contentView = [nextResponder contentView];
2688 nextResponder = contentView;
2690 if ([nextResponder isKindOfClass:[NSClipView class]]) {
2691 id documentView = [nextResponder documentView];
2693 nextResponder = documentView;
2695 if (nextResponder == self)
2698 Frame* coreFrame = core([self _frame]);
2699 bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable();
2700 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2701 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2703 return selectionIsEditable && nextResponderIsInWebView;
2706 - (void)addMouseMovedObserver
2708 if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications)
2711 // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2712 if (!([[self window] isKeyWindow]
2713 #if ENABLE(DASHBOARD_SUPPORT)
2714 || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]
2719 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2720 name:WKMouseMovedNotification() object:nil];
2721 [self _frameOrBoundsChanged];
2722 _private->observingMouseMovedNotifications = true;
2725 - (void)removeMouseMovedObserver
2727 #if ENABLE(DASHBOARD_SUPPORT)
2728 // Don't remove the observer if we're running the Dashboard.
2729 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2733 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2734 [self _removeMouseMovedObserverUnconditionally];
2737 - (void)addSuperviewObservers
2739 // We watch the bounds of our superview, so that we can do a layout when the size
2740 // of the superview changes. This is different from other scrollable things that don't
2741 // need this kind of thing because their layout doesn't change.
2743 // We need to pay attention to both height and width because our "layout" has to change
2744 // to extend the background the full height of the space and because some elements have
2745 // sizes that are based on the total size of the view.
2747 if (_private->observingSuperviewNotifications)
2750 NSView *superview = [self superview];
2751 if (!superview || ![self window])
2754 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2755 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview];
2756 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview];
2758 // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2759 // It will check the current size/scroll against the previous layout's size/scroll. We need to
2760 // do this here to catch the case where the WebView is laid out at one size, removed from its
2761 // window, resized, and inserted into another window. Our frame/bounds changed notifications
2762 // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2763 [self _frameOrBoundsChanged];
2765 _private->observingSuperviewNotifications = true;
2768 - (void)addWindowObservers
2770 if (_private->observingWindowNotifications)
2773 NSWindow *window = [self window];
2777 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2778 [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
2779 [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
2780 [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
2781 [notificationCenter addObserver:self selector:@selector(windowWillOrderOnScreen:) name:WKWindowWillOrderOnScreenNotification() object:window];
2783 _private->observingWindowNotifications = true;
2786 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2788 [self _removeSuperviewObservers];
2791 - (void)viewDidMoveToSuperview
2793 if ([self superview] != nil)
2794 [self addSuperviewObservers];
2797 static void _updateFocusedAndActiveStateTimerCallback(CFRunLoopTimerRef timer, void *info)
2799 WebHTMLView *view = (WebHTMLView *)info;
2800 [view _updateFocusedAndActiveState];
2803 - (void)viewWillMoveToWindow:(NSWindow *)window
2805 // Don't do anything if we aren't initialized. This happens
2806 // when decoding a WebView. When WebViews are decoded their subviews
2807 // are created by initWithCoder: and so won't be normally
2808 // initialized. The stub views are discarded by WebView.
2812 // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2813 [self _removeMouseMovedObserverUnconditionally];
2814 [self _removeWindowObservers];
2815 [self _removeSuperviewObservers];
2816 [self _cancelUpdateMouseoverTimer];
2817 [self _cancelUpdateFocusedAndActiveStateTimer];
2819 [[self _pluginController] stopAllPlugins];
2822 - (void)viewDidMoveToWindow
2824 // Don't do anything if we aren't initialized. This happens
2825 // when decoding a WebView. When WebViews are decoded their subviews
2826 // are created by initWithCoder: and so won't be normally
2827 // initialized. The stub views are discarded by WebView.
2828 if (!_private || _private->closed)
2831 [self _stopAutoscrollTimer];
2832 if ([self window]) {
2833 _private->lastScrollPosition = [[self superview] bounds].origin;
2834 [self addWindowObservers];
2835 [self addSuperviewObservers];
2836 [self addMouseMovedObserver];
2838 // Schedule this update, rather than making the call right now.
2839 // The reason is that placing the caret in the just-installed view requires
2840 // the HTML/XML document to be available on the WebCore side, but it is not
2841 // at the time this code is running. However, it will be there on the next
2842 // crank of the run loop. Doing this helps to make a blinking caret appear
2843 // in a new, empty window "automatic".
2844 if (!_private->updateFocusedAndActiveStateTimer) {
2845 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
2846 _private->updateFocusedAndActiveStateTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0,
2847 _updateFocusedAndActiveStateTimerCallback, &context);
2848 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateFocusedAndActiveStateTimer, kCFRunLoopDefaultMode);
2851 [[self _pluginController] startAllPlugins];
2853 _private->lastScrollPosition = NSZeroPoint;
2857 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2859 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2862 - (void)viewDidMoveToHostWindow
2864 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2868 - (void)addSubview:(NSView *)view
2870 [super addSubview:view];
2872 if ([WebPluginController isPlugInView:view])
2873 [[self _pluginController] addPlugin:view];
2876 - (void)willRemoveSubview:(NSView *)subview
2878 if ([WebPluginController isPlugInView:subview])
2879 [[self _pluginController] destroyPlugin:subview];
2881 [super willRemoveSubview:subview];
2884 - (void)reapplyStyles
2886 if (!_private->needsToApplyStyles)
2890 double start = CFAbsoluteTimeGetCurrent();
2893 if (Frame* coreFrame = core([self _frame])) {
2894 if (FrameView* coreView = coreFrame->view())
2895 coreView->setMediaType(_private->printing ? "print" : "screen");
2896 if (Document* document = coreFrame->document())
2897 document->setPrinting(_private->printing);
2898 coreFrame->reapplyStyles();
2902 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2903 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2906 _private->needsToApplyStyles = NO;
2909 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2910 // minPageWidth==0 implies a non-printing layout
2911 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
2913 [self reapplyStyles];
2915 if (!_private->needsLayout && ![[self _frame] _needsLayout])
2919 double start = CFAbsoluteTimeGetCurrent();
2922 LOG(View, "%@ doing layout", self);
2924 Frame* coreFrame = core([self _frame]);
2926 _private->needsLayout = NO;
2930 if (FrameView* coreView = coreFrame->view()) {
2931 if (minPageWidth > 0.0)
2932 coreView->forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize);
2934 coreView->forceLayout(!adjustViewSize);
2936 coreView->adjustViewSize();
2939 _private->needsLayout = NO;
2941 if (!_private->printing)
2942 _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
2945 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2946 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
2952 [self layoutToMinimumPageWidth:0.0f maximumPageWidth:0.0f adjustingViewSize:NO];
2955 // Deliver mouseup events to the DOM for button 2.
2956 - (void)rightMouseUp:(NSEvent *)event
2958 [super rightMouseUp:event];
2959 if (Frame* coreframe = core([self _frame]))
2960 coreframe->eventHandler()->mouseUp(event);
2963 - (NSMenu *)menuForEvent:(NSEvent *)event
2965 [_private->compController endRevertingChange:NO moveLeft:NO];
2967 _private->handlingMouseDownEvent = YES;
2968 BOOL handledEvent = NO;
2969 Frame* coreFrame = core([self _frame]);
2972 _private->handlingMouseDownEvent = NO;
2976 Page* page = coreFrame->page();
2980 page->contextMenuController()->clearContextMenu();
2981 // Match behavior of other browsers by sending an onmousedown event for right clicks.
2982 coreFrame->eventHandler()->mouseDown(event);
2983 handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformMouseEvent(event));
2984 _private->handlingMouseDownEvent = NO;
2989 ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
2993 NSArray* menuItems = coreMenu->platformDescription();
2995 if (menuItems && [menuItems count] > 0) {
2996 menu = [[[NSMenu alloc] init] autorelease];
2997 for (unsigned i = 0; i < [menuItems count]; i++)
2998 [menu addItem:[menuItems objectAtIndex:i]];
3004 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
3006 return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
3011 Frame* coreFrame = core([self _frame]);
3014 Document* document = coreFrame->document();
3018 document->setFocusedNode(0);
3023 return [[self _webView] drawsBackground];
3026 - (void)setNeedsDisplay:(BOOL)flag
3028 LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO");
3029 [super setNeedsDisplay:flag];
3032 - (void)setNeedsLayout: (BOOL)flag
3034 LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO");
3035 _private->needsLayout = flag;
3038 - (void)setNeedsToApplyStyles: (BOOL)flag
3040 LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO");
3041 _private->needsToApplyStyles = flag;
3044 - (void)drawSingleRect:(NSRect)rect
3046 [NSGraphicsContext saveGraphicsState];
3049 ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
3051 [(WebClipView *)[self superview] setAdditionalClip:rect];
3054 if ([self _transparentBackground]) {
3055 [[NSColor clearColor] set];
3059 [[self _frame] _drawRect:rect contentsOnly:YES];
3061 WebView *webView = [self _webView];
3063 // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
3064 // called after the WebView has closed. If the client did not properly close the WebView and set the
3065 // UIDelegate to nil, then the UIDelegate will be stale and this code will crash.
3066 static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
3067 if (version3OrLaterClient)
3068 [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
3070 if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight])
3071 [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]];
3073 [(WebClipView *)[self superview] resetAdditionalClip];
3075 [NSGraphicsContext restoreGraphicsState];
3076 } @catch (NSException *localException) {
3077 [(WebClipView *)[self superview] resetAdditionalClip];
3078 [NSGraphicsContext restoreGraphicsState];
3079 LOG_ERROR("Exception caught while drawing: %@", localException);
3080 [localException raise];
3084 - (void)drawRect:(NSRect)rect
3086 ASSERT_MAIN_THREAD();
3087 LOG(View, "%@ drawing", self);
3089 const NSRect *rects;
3091 [self getRectsBeingDrawn:&rects count:&count];
3093 BOOL subviewsWereSetAside = _private->subviewsSetAside;
3094 if (subviewsWereSetAside)
3095 [self _restoreSubviews];
3098 double start = CFAbsoluteTimeGetCurrent();
3101 if ([[self _webView] _mustDrawUnionedRect:rect singleRects:rects count:count])
3102 [self drawSingleRect:rect];
3104 for (int i = 0; i < count; ++i)
3105 [self drawSingleRect:rects[i]];
3108 double thisTime = CFAbsoluteTimeGetCurrent() - start;
3109 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
3112 if (subviewsWereSetAside)
3113 [self _setAsideSubviews];
3115 #if USE(ACCELERATED_COMPOSITING)
3116 if ([[self _webView] _needsOneShotDrawingSynchronization]) {
3117 // Disable screen updates so that drawing into the NSView and
3118 // CALayer updates appear on the screen at the same time.
3119 [[self window] disableScreenUpdatesUntilFlush];
3120 [CATransaction flush];
3121 [[self _webView] _setNeedsOneShotDrawingSynchronization:NO];
3126 // Turn off the additional clip while computing our visibleRect.
3127 - (NSRect)visibleRect
3129 if (!([[self superview] isKindOfClass:[WebClipView class]]))
3130 return [super visibleRect];
3132 WebClipView *clipView = (WebClipView *)[self superview];
3134 BOOL hasAdditionalClip = [clipView hasAdditionalClip];
3135 if (!hasAdditionalClip) {
3136 return [super visibleRect];
3139 NSRect additionalClip = [clipView additionalClip];
3140 [clipView resetAdditionalClip];
3141 NSRect visibleRect = [super visibleRect];
3142 [clipView setAdditionalClip:additionalClip];
3151 - (void)windowDidBecomeKey:(NSNotification *)notification
3153 NSWindow *keyWindow = [notification object];
3155 if (keyWindow == [self window])
3156 [self addMouseMovedObserver];
3158 if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
3159 [self _updateFocusedAndActiveState];
3162 - (void)windowDidResignKey:(NSNotification *)notification
3164 NSWindow *formerKeyWindow = [notification object];
3166 if (formerKeyWindow == [self window])
3167 [self removeMouseMovedObserver];
3169 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
3170 [self _updateFocusedAndActiveState];
3171 [_private->compController endRevertingChange:NO moveLeft:NO];
3175 - (void)windowWillClose:(NSNotification *)notification
3177 [_private->compController endRevertingChange:NO moveLeft:NO];
3178 [[self _pluginController] destroyAllPlugins];
3181 - (void)windowWillOrderOnScreen:(NSNotification *)notification
3183 if (![[self _webView] shouldUpdateWhileOffscreen])
3184 [self setNeedsDisplay:YES];
3187 - (void)scrollWheel:(NSEvent *)event
3190 Frame* frame = core([self _frame]);
3191 if (!frame || !frame->eventHandler()->wheelEvent(event))
3192 [super scrollWheel:event];
3196 - (BOOL)_isSelectionEvent:(NSEvent *)event
3198 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3199 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
3202 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3204 NSView *hitView = [self _hitViewForEvent:event];
3205 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3207 #if ENABLE(DASHBOARD_SUPPORT)
3208 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
3213 bool result = false;
3214 if (Frame* coreFrame = core([hitHTMLView _frame])) {
3215 coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
3216 [hitHTMLView _setMouseDownEvent:event];
3217 if ([hitHTMLView _isSelectionEvent:event])
3218 result = coreFrame->eventHandler()->eventMayStartDrag(event);
3219 [hitHTMLView _setMouseDownEvent:nil];
3223 return [hitView acceptsFirstMouse:event];
3226 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3228 NSView *hitView = [self _hitViewForEvent:event];
3229 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3231 bool result = false;
3232 if ([hitHTMLView _isSelectionEvent:event])
3233 if (Frame* coreFrame = core([hitHTMLView _frame])) {
3234 [hitHTMLView _setMouseDownEvent:event];
3235 result = coreFrame->eventHandler()->eventMayStartDrag(event);
3236 [hitHTMLView _setMouseDownEvent:nil];
3240 return [hitView shouldDelayWindowOrderingForEvent:event];
3243 - (void)mouseDown:(NSEvent *)event
3245 RetainPtr<WebHTMLView> protector = self;
3246 if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
3249 _private->handlingMouseDownEvent = YES;
3251 // Record the mouse down position so we can determine drag hysteresis.
3252 [self _setMouseDownEvent:event];
3254 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3255 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3258 [_private->compController endRevertingChange:NO moveLeft:NO];
3260 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
3261 // We don't want to pass them along to KHTML a second time.
3262 if (!([event modifierFlags] & NSControlKeyMask)) {
3263 _private->ignoringMouseDraggedEvents = NO;
3265 // Don't do any mouseover while the mouse is down.
3266 [self _cancelUpdateMouseoverTimer];
3268 // Let WebCore get a chance to deal with the event. This will call back to us
3269 // to start the autoscroll timer if appropriate.
3270 if (Frame* coreframe = core([self _frame]))
3271 coreframe->eventHandler()->mouseDown(event);
3275 _private->handlingMouseDownEvent = NO;
3278 - (void)dragImage:(NSImage *)dragImage
3280 offset:(NSSize)offset
3281 event:(NSEvent *)event
3282 pasteboard:(NSPasteboard *)pasteboard
3284 slideBack:(BOOL)slideBack
3286 ASSERT(self == [self _topHTMLView]);
3287 [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
3290 - (void)mouseDragged:(NSEvent *)event
3292 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3293 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3298 if (!_private->ignoringMouseDraggedEvents)
3299 if (Frame* coreframe = core([self _frame]))
3300 coreframe->eventHandler()->mouseDragged(event);
3305 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
3307 ASSERT(![self _webView] || [self _isTopHTMLView]);
3309 Page *page = core([self _webView]);
3312 return NSDragOperationNone;
3314 if (page->dragController()->dragOperation() == DragOperationNone)
3315 return NSDragOperationGeneric | NSDragOperationCopy;
3317 return (NSDragOperation)page->dragController()->dragOperation();
3320 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
3322 ASSERT(![self _webView] || [self _isTopHTMLView]);
3324 NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
3325 NSPoint windowMouseLoc = windowImageLoc;
3327 if (Page* page = core([self _webView])) {
3328 DragController* dragController = page->dragController();
3329 NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3332 [[self _frame] _dragSourceMovedTo:windowMouseLoc];
3335 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
3337 ASSERT(![self _webView] || [self _isTopHTMLView]);
3339 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
3340 NSPoint windowMouseLoc = windowImageLoc;
3342 if (Page* page = core([self _webView])) {
3343 DragController* dragController = page->dragController();
3344 windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3345 dragController->dragEnded();
3348 [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation];
3350 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3351 _private->ignoringMouseDraggedEvents = YES;
3353 // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
3354 // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
3355 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
3356 location:windowMouseLoc
3357 modifierFlags:[[NSApp currentEvent] modifierFlags]
3358 timestamp:[NSDate timeIntervalSinceReferenceDate]
3359 windowNumber:[[self window] windowNumber]
3360 context:[[NSApp currentEvent] context]
3361 eventNumber:0 clickCount:0 pressure:0];
3362 [self mouseUp:fakeEvent]; // This will also update the mouseover state.
3365 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3367 NSFileWrapper *wrapper = nil;
3368 NSURL *draggingImageURL = nil;
3370 if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) {
3372 SharedBuffer *buffer = static_cast<CachedResource*>(tiffResource)->data();
3374 goto noPromisedData;
3376 NSData *data = buffer->createNSData();
3377 NSURLResponse *response = tiffResource->response().nsURLResponse();
3378 draggingImageURL = [response URL];
3379 wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
3380 NSString* filename = [response suggestedFilename];
3381 NSString* trueExtension(tiffResource->image()->filenameExtension());
3382 if (![filename matchesExtensionEquivalent:trueExtension])
3383 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension];
3384 [wrapper setPreferredFilename:filename];
3390 ASSERT(![self _webView] || [self _isTopHTMLView]);
3391 Page* page = core([self _webView]);
3393 //If a load occurs midway through a drag, the view may be detached, which gives
3394 //us no ability to get to the original Page, so we cannot access any drag state
3395 //FIXME: is there a way to recover?
3399 const KURL& imageURL = page->dragController()->draggingImageURL();
3400 ASSERT(!imageURL.isEmpty());
3401 draggingImageURL = imageURL;
3403 wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL];
3406 if (wrapper == nil) {
3407 LOG_ERROR("Failed to create image file.");
3411 // FIXME: Report an error if we fail to create a file.
3412 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3413 path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
3414 if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
3415 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
3417 if (draggingImageURL)
3418 [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path];
3420 return [NSArray arrayWithObject:[path lastPathComponent]];
3423 - (void)mouseUp:(NSEvent *)event
3425 [self _setMouseDownEvent:nil];
3427 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3428 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3433 [self _stopAutoscrollTimer];
3434 if (Frame* coreframe = core([self _frame]))
3435 coreframe->eventHandler()->mouseUp(event);
3436 [self _updateMouseoverWithFakeEvent];
3441 - (void)mouseMovedNotification:(NSNotification *)notification
3443 [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
3446 // returning YES from this method is the way we tell AppKit that it is ok for this view
3447 // to be in the key loop even when "tab to all controls" is not on.
3448 - (BOOL)needsPanelToBecomeKey
3453 - (BOOL)becomeFirstResponder
3455 NSSelectionDirection direction = NSDirectSelection;
3456 if (![[self _webView] _isPerformingProgrammaticFocus])
3457 direction = [[self window] keyViewSelectionDirection];
3459 [self _updateFocusedAndActiveState];
3460 [self _updateFontPanel];
3462 Frame* frame = core([self _frame]);
3466 frame->editor()->setStartNewKillRingSequence(true);
3468 if (direction == NSDirectSelection)
3471 Page* page = frame->page();
3475 page->focusController()->setFocusedFrame(frame);
3476 if (Document* document = frame->document())
3477 document->setFocusedNode(0);
3478 page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward,
3479 frame->eventHandler()->currentKeyboardEvent().get());
3483 - (BOOL)resignFirstResponder
3485 BOOL resign = [super resignFirstResponder];
3487 _private->resigningFirstResponder = YES;
3488 [_private->compController endRevertingChange:NO moveLeft:NO];
3489 if (![self maintainsInactiveSelection]) {
3491 if (![[self _webView] _isPerformingProgrammaticFocus])
3494 [self _updateFocusedAndActiveState];
3495 _private->resigningFirstResponder = NO;
3500 - (void)setDataSource:(WebDataSource *)dataSource
3503 if (_private->dataSource != dataSource) {
3504 ASSERT(!_private->closed);
3505 BOOL hadDataSource = _private->dataSource != nil;
3507 [dataSource retain];
3508 [_private->dataSource release];
3509 _private->dataSource = dataSource;
3510 [_private->pluginController setDataSource:dataSource];
3513 [self addMouseMovedObserver];
3517 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3521 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3522 // key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3523 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3524 - (void)updateCell:(NSCell*)cell
3528 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3529 // pageWidth != 0 implies we will relayout to a new width
3530 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
3532 WebFrame *frame = [self _frame];
3533 NSArray *subframes = [frame childFrames];
3534 unsigned n = [subframes count];
3536 for (i = 0; i != n; ++i) {
3537 WebFrame *subframe = [subframes objectAtIndex:i];
3538 WebFrameView *frameView = [subframe frameView];
3539 if ([[subframe _dataSource] _isDocumentHTML]) {
3540 [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:adjustViewSize];
3544 if (printing != _private->printing) {
3545 [_private->pageRects release];
3546 _private->pageRects = nil;
3547 _private->printing = printing;
3549 _private->avoidingPrintOrphan = NO;
3550 [self setNeedsToApplyStyles:YES];
3551 [self setNeedsLayout:YES];
3552 [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
3554 // Can't do this when starting printing or nested printing won't work, see 3491427.
3555 [self setNeedsDisplay:NO];
3560 - (BOOL)canPrintHeadersAndFooters
3565 // This is needed for the case where the webview is embedded in the view that's being printed.
3566 // It shouldn't be called when the webview is being printed directly.
3567 - (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit
3569 // This helps when we print as part of a larger print process.
3570 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3571 BOOL wasInPrintingMode = _private->printing;
3572 if (!wasInPrintingMode)
3573 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3575 float newBottomFloat = *newBottom;
3576 if (FrameView* view = core([self _frame])->view())
3577 view->adjustPageHeight(&newBottomFloat, oldTop, oldBottom, bottomLimit);
3580 // If the new bottom is equal to the old bottom (when both are treated as floats), we just copy
3581 // oldBottom over to newBottom. This prevents rounding errors that can occur when converting newBottomFloat to a double.
3582 if (fabs((float)oldBottom - newBottomFloat) <= std::numeric_limits<float>::epsilon())
3583 *newBottom = oldBottom;
3586 *newBottom = newBottomFloat;
3588 if (!wasInPrintingMode) {
3589 NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation];
3590 if (currenPrintOperation)
3591 // delay _setPrinting:NO until back to main loop as this method may get called repeatedly
3592 [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0];
3594 // not sure if this is actually ever invoked, it probably shouldn't be
3595 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3599 - (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
3601 NSPrintInfo *printInfo = [printOperation printInfo];
3602 return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
3605 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3607 float viewWidth = NSWidth([self bounds]);
3608 if (viewWidth < 1) {
3609 LOG_ERROR("%@ has no width when printing", self);
3613 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3614 float maxShrinkToFitScaleFactor = 1.0f / PrintingMaximumShrinkFactor;
3615 float shrinkToFitScaleFactor = [self _availablePaperWidthForPrintOperation:printOperation]/viewWidth;
3616 float shrinkToAvoidOrphan = _private->avoidingPrintOrphan ? (1.0f / PrintingOrphanShrinkAdjustment) : 1.0f;
3617 return userScaleFactor * MAX(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor) * shrinkToAvoidOrphan;
3620 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3621 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3622 // if AppKit makes it SPI/API.
3623 - (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3625 return [self _scaleFactorForPrintOperation:printOperation];
3628 // This is used for Carbon printing. At some point we might want to make this public API.
3629 - (void)setPageWidthForPrinting:(float)pageWidth
3631 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3632 [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
3635 - (void)_endPrintMode
3637 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
3638 [[self window] setAutodisplay:YES];
3641 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3643 ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3644 NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3645 if (initiatingOperation == currentOperation) {
3646 // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3647 // being extra paranoid here since the printing code is so fragile. Delay the cleanup
3649 ASSERT_NOT_REACHED();
3650 [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3651 } else if ([currentOperation view] == self) {
3652 // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3653 // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3654 // fragile. Do nothing, because we don't want to break the print job currently in progress, and
3655 // the print job currently in progress is responsible for its own cleanup.
3656 ASSERT_NOT_REACHED();
3658 // The print job that kicked off this delayed call has finished, and this view is not being
3659 // printed again. We expect that no other print job has started. Since this delayed call wasn't
3660 // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3661 // the print mode here.
3662 ASSERT(currentOperation == nil);
3663 [self _endPrintMode];
3667 // Return the number of pages available for printing
3668 - (BOOL)knowsPageRange:(NSRangePointer)range
3670 // Must do this explicit display here, because otherwise the view might redisplay while the print
3671 // sheet was up, using printer fonts (and looking different).
3672 [self displayIfNeeded];
3673 [[self window] setAutodisplay:NO];
3675 // If we are a frameset just print with the layout we have onscreen, otherwise relayout
3676 // according to the paper size
3677 float minLayoutWidth = 0.0f;
3678 float maxLayoutWidth = 0.0f;
3679 Frame* frame = core([self _frame]);
3682 if (!frame->document() || !frame->document()->isFrameSet()) {
3683 float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
3684 minLayoutWidth = paperWidth * PrintingMinimumShrinkFactor;
3685 maxLayoutWidth = paperWidth * PrintingMaximumShrinkFactor;
3687 [self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; // will relayout
3688 NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3689 // Certain types of errors, including invalid page ranges, can cause beginDocument and
3690 // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3691 // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3692 // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3693 // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3694 // which is after beginDocument and endDocument would be called.
3695 [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3696 [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3698 // There is a theoretical chance that someone could do some drawing between here and endDocument,
3699 // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3700 // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3702 range->location = 1;
3703 float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3704 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3705 [_private->pageRects release];
3706 float fullPageHeight = floorf([self _calculatePrintHeight]/totalScaleFactor);
3707 NSArray *newPageRects = [[self _frame] _computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3708 printHeight:fullPageHeight];
3710 // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3711 // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3712 // a blank page (with correct-looking header and footer if that option is on), which matches
3713 // the behavior of IE and Camino at least.
3714 if ([newPageRects count] == 0)
3715 newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3716 else if ([newPageRects count] > 1) {
3717 // If the last page is a short orphan, try adjusting the print height slightly to see if this will squeeze the
3718 // content onto one fewer page. If it does, use the adjusted scale. If not, use the original scale.
3719 float lastPageHeight = NSHeight([[newPageRects lastObject] rectValue]);
3720 if (lastPageHeight/fullPageHeight < LastPrintedPageOrphanRatio) {
3721 NSArray *adjustedPageRects = [[self _frame] _computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3722 printHeight:fullPageHeight*PrintingOrphanShrinkAdjustment];
3723 // Use the adjusted rects only if the page count went down
3724 if ([adjustedPageRects count] < [newPageRects count]) {
3725 newPageRects = adjustedPageRects;
3726 _private->avoidingPrintOrphan = YES;
3731 _private->pageRects = [newPageRects retain];
3733 range->length = [_private->pageRects count];
3738 // Return the drawing rectangle for a particular page number
3739 - (NSRect)rectForPage:(NSInteger)page
3741 return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3744 - (void)drawPageBorderWithSize:(NSSize)borderSize
3746 ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));
3747 [[self _webView] _drawHeaderAndFooter];
3750 - (void)beginDocument
3753 // From now on we'll get a chance to call _endPrintMode in either beginDocument or
3754 // endDocument, so we can cancel the "just in case" pending call.
3755 [NSObject cancelPreviousPerformRequestsWithTarget:self
3756 selector:@selector(_delayedEndPrintMode:)
3757 object:[NSPrintOperation currentOperation]];
3758 [super beginDocument];
3759 } @catch (NSException *localException) {
3760 // Exception during [super beginDocument] means that endDocument will not get called,
3761 // so we need to clean up our "print mode" here.
3762 [self _endPrintMode];
3768 [super endDocument];
3769 // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO
3770 [self _endPrintMode];
3773 - (void)keyDown:(NSEvent *)event
3775 RetainPtr<WebHTMLView> selfProtector = self;
3776 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
3778 BOOL callSuper = NO;
3780 [_private->keyDownEvent release];
3781 _private->keyDownEvent = [event retain];
3783 BOOL completionPopupWasOpen = _private->compController && [_private->compController popupWindowIsOpen];
3784 Frame* coreFrame = core([self _frame]);
3785 if (!eventWasSentToWebCore && coreFrame && coreFrame->eventHandler()->keyEvent(event)) {
3786 // WebCore processed a key event, bail on any preexisting complete: UI
3787 if (completionPopupWasOpen)
3788 [_private->compController endRevertingChange:YES moveLeft:NO];
3789 } else if (!_private->compController || ![_private->compController filterKeyDown:event]) {
3790 // Not consumed by complete: popup window
3791 [_private->compController endRevertingChange:YES moveLeft:NO];
3795 [super keyDown:event];
3797 [NSCursor setHiddenUntilMouseMoves:YES];
3800 - (void)keyUp:(NSEvent *)event
3802 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
3804 RetainPtr<WebHTMLView> selfProtector = self;
3805 Frame* coreFrame = core([self _frame]);
3806 if (coreFrame && !eventWasSentToWebCore)
3807 coreFrame->eventHandler()->keyEvent(event);
3809 [super keyUp:event];
3812 - (void)flagsChanged:(NSEvent *)event
3814 Frame* coreFrame = core([self _frame]);
3816 coreFrame->eventHandler()->capsLockStateMayHaveChanged();
3818 RetainPtr<WebHTMLView> selfProtector = self;
3820 unsigned short keyCode = [event keyCode];
3822 //Don't make an event from the num lock and function keys
3823 if (coreFrame && keyCode != 0 && keyCode != 10 && keyCode != 63) {
3824 coreFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(event));
3828 [super flagsChanged:event];
3831 - (id)accessibilityAttributeValue:(NSString*)attributeName
3833 if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
3834 id accTree = [[self _frame] _accessibilityTree];
3836 return [NSArray arrayWithObject:accTree];
3839 return [super accessibilityAttributeValue:attributeName];
3842 - (id)accessibilityFocusedUIElement
3844 id accTree = [[self _frame] _accessibilityTree];
3846 return [accTree accessibilityFocusedUIElement];
3850 - (id)accessibilityHitTest:(NSPoint)point
3852 id accTree = [[self _frame] _accessibilityTree];
3854 NSPoint windowCoord = [[self window] convertScreenToBase:point];
3855 return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
3860 - (id)_accessibilityParentForSubview:(NSView *)subview
3862 id accTree = [[self _frame] _accessibilityTree];
3865 id parent = [accTree _accessibilityParentForSubview:subview];
3871 - (void)centerSelectionInVisibleArea:(id)sender
3875 if (Frame* coreFrame = core([self _frame]))
3876 coreFrame->revealSelection(ScrollAlignment::alignCenterAlways);
3879 - (NSData *)_selectionStartFontAttributesAsRTF
3881 Frame* coreFrame = core([self _frame]);
3882 NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x"
3883 attributes:coreFrame ? coreFrame->fontAttributesForSelectionStart() : nil];
3884 NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
3889 - (NSDictionary *)_fontAttributesFromFontPasteboard
3891 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
3892 if (fontPasteboard == nil)
3894 NSData *data = [fontPasteboard dataForType:NSFontPboardType];
3895 if (data == nil || [data length] == 0)
3897 // NSTextView does something more efficient by parsing the attributes only, but that's not available in API.
3898 NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease];
3899 if (string == nil || [string length] == 0)
3901 return [string fontAttributesInRange:NSMakeRange(0, 1)];
3904 - (DOMCSSStyleDeclaration *)_emptyStyle
3906 return [[[self _frame] DOMDocument] createCSSStyleDeclaration];
3909 - (NSString *)_colorAsString:(NSColor *)color
3911 NSColor *rgbColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
3912 // FIXME: If color is non-nil and rgbColor is nil, that means we got some kind
3913 // of fancy color that can't be converted to RGB. Changing that to "transparent"
3914 // might not be great, but it's probably OK.
3915 if (rgbColor == nil)
3916 return @"transparent";
3917 float r = [rgbColor redComponent];
3918 float g = [rgbColor greenComponent];
3919 float b = [rgbColor blueComponent];
3920 float a = [rgbColor alphaComponent];
3922 return @"transparent";
3923 if (r == 0 && g == 0 && b == 0 && a == 1)
3925 if (r == 1 && g == 1 && b == 1 && a == 1)
3927 // FIXME: Lots more named colors. Maybe we could use the table in WebCore?
3929 return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255];
3930 return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a];
3933 - (NSString *)_shadowAsString:(NSShadow *)shadow
3937 NSSize offset = [shadow shadowOffset];
3938 float blurRadius = [shadow shadowBlurRadius];
3939 if (offset.width == 0 && offset.height == 0 && blurRadius == 0)
3941 NSColor *color = [shadow shadowColor];
3944 // FIXME: Handle non-integral values here?
3945 if (blurRadius == 0)
3946 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height];
3947 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius];
3950 - (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary
3952 DOMCSSStyleDeclaration *style = [self _emptyStyle];
3954 NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName];
3955 [style setBackgroundColor:[self _colorAsString:color]];
3957 NSFont *font = [dictionary objectForKey:NSFontAttributeName];
3959 [style setFontFamily:@"Helvetica"];
3960 [style setFontSize:@"12px"];
3961 [style setFontWeight:@"normal"];
3962 [style setFontStyle:@"normal"];
3964 NSFontManager *fm = [NSFontManager sharedFontManager];
3965 // FIXME: Need more sophisticated escaping code if we want to handle family names
3966 // with characters like single quote or backslash in their names.
3967 [style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]];
3968 [style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]];
3969 // FIXME: Map to the entire range of CSS weight values.
3970 if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT)
3971 [style setFontWeight:@"bold"];
3973 [style setFontWeight:@"normal"];
3974 if ([fm traitsOfFont:font] & NSItalicFontMask)
3975 [style setFontStyle:@"italic"];
3977 [style setFontStyle:@"normal"];
3980 color = [dictionary objectForKey:NSForegroundColorAttributeName];
3981 [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"];
3983 NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName];
3984 [style setTextShadow:[self _shadowAsString:shadow]];
3986 int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue];
3988 int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue];
3989 if (superscriptInt > 0)
3990 [style setVerticalAlign:@"super"];
3991 else if (superscriptInt < 0)
3992 [style setVerticalAlign:@"sub"];
3994 [style setVerticalAlign:@"baseline"];
3995 int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue];
3996 // FIXME: Underline wins here if we have both (see bug 3790443).
3997 if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone)
3998 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
3999 else if (underlineInt == NSUnderlineStyleNone)
4000 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
4002 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
4007 - (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
4009 if (Frame* coreFrame = core([self _frame]))
4010 coreFrame->editor()->applyStyleToSelection(core(style), undoAction);
4013 - (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
4015 if (Frame* coreFrame = core([self _frame]))
4016 coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction);
4019 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
4021 ASSERT([self _webView]);
4022 if (![[[self _webView] preferences] respectStandardStyleKeyEquivalents])
4025 if (![self _canEdit])
4028 if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
4031 NSString *string = [event characters];
4032 if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
4033 [self executeCoreCommandByName:"ToggleBold"];
4036 if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
4037 [self executeCoreCommandByName:"ToggleItalic"];
4044 - (BOOL)performKeyEquivalent:(NSEvent *)event
4046 if ([self _handleStyleKeyEquivalent:event])
4049 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
4052 [_private->keyDownEvent release];
4053 _private->keyDownEvent = [event retain];
4057 // Pass command-key combos through WebCore if there is a key binding available for
4058 // this event. This lets web pages have a crack at intercepting command-modified keypresses.
4059 // But don't do it if we have already handled the event.
4060 // Pressing Esc results in a fake event being sent - don't pass it to WebCore.
4061 if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder])
4062 if (Frame* frame = core([self _frame]))
4063 ret = frame->eventHandler()->keyEvent(event);
4066 ret = [super performKeyEquivalent:event];
4073 - (void)copyFont:(id)sender
4077 // Put RTF with font attributes on the pasteboard.
4078 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
4079 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
4080 [fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil];
4081 [fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType];
4084 - (void)pasteFont:(id)sender
4088 // Read RTF with font attributes from the pasteboard.
4089 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
4090 [self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:EditActionPasteFont];
4093 - (void)pasteAsRichText:(id)sender
4097 // Since rich text always beats plain text when both are on the pasteboard, it's not
4098 // clear how this is different from plain old paste.
4099 [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO];
4102 - (NSFont *)_originalFontA
4104 return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f];
4107 - (NSFont *)_originalFontB
4109 return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:NSFontItalicTrait weight:STANDARD_BOLD_WEIGHT size:12.0f];
4112 - (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b
4114 // Since there's no way to directly ask NSFontManager what style change it's going to do
4115 // we instead pass two "specimen" fonts to it and let it change them. We then deduce what
4116 // style change it was doing by looking at what happened to each of the two fonts.
4117 // So if it was making the text bold, both fonts will be bold after the fact.
4119 if (a == nil || b == nil)
4122 NSFontManager *fm = [NSFontManager sharedFontManager];
4124 NSFont *oa = [self _originalFontA];
4126 NSString *aFamilyName = [a familyName];
4127 NSString *bFamilyName = [b familyName];
4129 int aPointSize = (int)[a pointSize];
4130 int bPointSize = (int)[b pointSize];
4132 int aWeight = [fm weightOfFont:a];
4133 int bWeight = [fm weightOfFont:b];