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 @interface WebResponderChainSink : NSResponder {
136 NSResponder* _lastResponderInChain;
137 BOOL _receivedUnhandledCommand;
139 - (id)initWithResponderChain:(NSResponder *)chain;
141 - (BOOL)receivedUnhandledCommand;
144 static IMP oldSetCursorIMP = NULL;
146 #ifdef BUILDING_ON_TIGER
147 static IMP oldResetCursorRectsIMP = NULL;
148 static BOOL canSetCursor = YES;
150 static void resetCursorRects(NSWindow* self, SEL cmd)
152 NSPoint point = [self mouseLocationOutsideOfEventStream];
153 NSView* view = [[self _web_borderView] hitTest:point];
154 if ([view isKindOfClass:[WebHTMLView class]]) {
155 WebHTMLView *htmlView = (WebHTMLView*)view;
156 NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
157 NSDictionary *dict = [htmlView elementAtPoint:localPoint allowShadowContent:NO];
158 DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
159 if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
160 ![element isKindOfClass:[DOMHTMLEmbedElement class]])
163 oldResetCursorRectsIMP(self, cmd);
167 static void setCursor(NSCursor* self, SEL cmd)
170 oldSetCursorIMP(self, cmd);
173 static void setCursor(NSWindow* self, SEL cmd, NSPoint point)
175 NSView* view = [[self _web_borderView] hitTest:point];
176 if ([view isKindOfClass:[WebHTMLView class]]) {
177 WebHTMLView *htmlView = (WebHTMLView*)view;
178 NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
179 NSDictionary *dict = [htmlView elementAtPoint:localPoint allowShadowContent:NO];
180 DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
181 if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
182 ![element isKindOfClass:[DOMHTMLEmbedElement class]])
185 oldSetCursorIMP(self, cmd, point);
189 #if USE(ACCELERATED_COMPOSITING)
190 @interface WebLayerHostingView : NSView
193 @implementation WebLayerHostingView
194 // Empty NSViews intercept rightMouseDown: to do context menu handling, but we need the WebLayerHostingView to
195 // let right mouse clicks through.
196 - (void)rightMouseDown:(NSEvent *)theEvent
198 [[self nextResponder] performSelector:_cmd withObject:theEvent];
201 #endif // USE(ACCELERATED_COMPOSITING)
205 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
207 extern NSString *NSMarkedClauseSegmentAttributeName;
208 extern NSString *NSTextInputReplacementRangeAttributeName;
211 @interface NSView (WebNSViewDetails)
212 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
213 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
214 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView;
215 - (NSRect)_dirtyRect;
216 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
217 - (void)_propagateDirtyRectsToOpaqueAncestors;
218 - (void)_windowChangedKeyState;
221 @interface NSApplication (WebNSApplicationDetails)
222 - (void)speakString:(NSString *)string;
225 @interface NSWindow (WebNSWindowDetails)
226 - (id)_newFirstResponderAfterResigning;
227 - (void)_setForceActiveControls:(BOOL)flag;
230 @interface NSAttributedString (WebNSAttributedStringDetails)
231 - (id)_initWithDOMRange:(DOMRange *)range;
232 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
235 @interface NSSpellChecker (WebNSSpellCheckerDetails)
236 - (void)learnWord:(NSString *)word;
239 // By imaging to a width a little wider than the available pixels,
240 // thin pages will be scaled down a little, matching the way they
241 // print in IE and Camino. This lets them use fewer sheets than they
242 // would otherwise, which is presumably why other browsers do this.
243 // Wide pages will be scaled down more than this.
244 #define PrintingMinimumShrinkFactor 1.25f
246 // This number determines how small we are willing to reduce the page content
247 // in order to accommodate the widest line. If the page would have to be
248 // reduced smaller to make the widest line fit, we just clip instead (this
249 // behavior matches MacIE and Mozilla, at least)
250 #define PrintingMaximumShrinkFactor 2.0f
252 // This number determines how short the last printed page of a multi-page print session
253 // can be before we try to shrink the scale in order to reduce the number of pages, and
254 // thus eliminate the orphan.
255 #define LastPrintedPageOrphanRatio 0.1f
257 // This number determines the amount the scale factor is adjusted to try to eliminate orphans.
258 // It has no direct mathematical relationship to LastPrintedPageOrphanRatio, due to variable
259 // numbers of pages, logic to avoid breaking elements, and CSS-supplied hard page breaks.
260 #define PrintingOrphanShrinkAdjustment 1.1f
262 #define AUTOSCROLL_INTERVAL 0.1f
264 #define DRAG_LABEL_BORDER_X 4.0f
265 //Keep border_y in synch with DragController::LinkDragBorderInset
266 #define DRAG_LABEL_BORDER_Y 2.0f
267 #define DRAG_LABEL_RADIUS 5.0f
268 #define DRAG_LABEL_BORDER_Y_OFFSET 2.0f
270 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120.0f
271 #define MAX_DRAG_LABEL_WIDTH 320.0f
273 #define DRAG_LINK_LABEL_FONT_SIZE 11.0f
274 #define DRAG_LINK_URL_FONT_SIZE 10.0f
276 // Any non-zero value will do, but using something recognizable might help us debug some day.
277 #define TRACKING_RECT_TAG 0xBADFACE
279 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
280 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
282 #define STANDARD_WEIGHT 5
283 #define MIN_BOLD_WEIGHT 7
284 #define STANDARD_BOLD_WEIGHT 9
287 #define WebDataProtocolScheme @"webkit-fake-url"
289 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
290 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
291 @interface WebCoreScrollView : NSScrollView
294 @implementation WebCoreScrollView
297 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
298 static BOOL forceNSViewHitTest;
300 // 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])
301 static BOOL forceWebHTMLViewHitTest;
303 static WebHTMLView *lastHitView;
305 // We need this to be able to safely reference the CachedImage for the promised drag data
306 static CachedResourceClient* promisedDataClient()
308 static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
309 return staticCachedResourceClient;
312 @interface WebHTMLView (WebHTMLViewFileInternal)
313 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
314 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
315 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
316 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
317 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
318 - (void)_removeMouseMovedObserverUnconditionally;
319 - (void)_removeSuperviewObservers;
320 - (void)_removeWindowObservers;
321 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
322 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
323 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
324 - (float)_calculatePrintHeight;
325 - (DOMRange *)_selectedRange;
326 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
327 - (NSView *)_hitViewForEvent:(NSEvent *)event;
328 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
329 - (DOMRange *)_documentRange;
330 - (void)_setMouseDownEvent:(NSEvent *)event;
331 - (WebHTMLView *)_topHTMLView;
332 - (BOOL)_isTopHTMLView;
333 - (void)_web_setPrintingModeRecursive;
334 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
335 - (void)_web_clearPrintingModeRecursive;
338 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
339 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize;
342 @class NSTextInputContext;
343 @interface NSResponder (AppKitDetails)
344 - (NSTextInputContext *)inputContext;
347 @interface NSObject (NSTextInputContextDetails)
348 - (BOOL)wantsToHandleMouseEvents;
349 - (BOOL)handleMouseEvent:(NSEvent *)event;
352 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
353 - (void)_updateSelectionForInputManager;
356 @interface WebHTMLView (WebEditingStyleSupport)
357 - (DOMCSSStyleDeclaration *)_emptyStyle;
358 - (NSString *)_colorAsString:(NSColor *)color;
361 @interface NSView (WebHTMLViewFileInternal)
362 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
365 @interface NSMutableDictionary (WebHTMLViewFileInternal)
366 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
369 // Handles the complete: text command
370 @interface WebTextCompleteController : NSObject <NSTableViewDelegate, NSTableViewDataSource> {
373 NSWindow *_popupWindow;
374 NSTableView *_tableView;
375 NSArray *_completions;
376 NSString *_originalString;
379 - (id)initWithHTMLView:(WebHTMLView *)view;
380 - (void)doCompletion;
381 - (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft;
382 - (BOOL)popupWindowIsOpen;
383 - (BOOL)filterKeyDown:(NSEvent *)event;
384 - (void)_reflectSelection;
387 struct WebHTMLViewInterpretKeyEventsParameters {
388 KeyboardEvent* event;
389 BOOL eventWasHandled;
390 BOOL shouldSaveCommand;
391 // The Input Method may consume an event and not tell us, in
392 // which case we should not bubble the event up the DOM
396 @interface WebHTMLViewPrivate : NSObject {
400 BOOL needsToApplyStyles;
401 BOOL ignoringMouseDraggedEvents;
403 BOOL avoidingPrintOrphan;
404 BOOL observingMouseMovedNotifications;
405 BOOL observingSuperviewNotifications;
406 BOOL observingWindowNotifications;
407 BOOL resigningFirstResponder;
410 BOOL subviewsSetAside;
412 #if USE(ACCELERATED_COMPOSITING)
413 NSView *layerHostingView;
416 NSEvent *mouseDownEvent; // Kept after handling the event.
417 BOOL handlingMouseDownEvent;
418 NSEvent *keyDownEvent; // Kept after handling the event.
420 NSSize lastLayoutSize;
422 NSPoint lastScrollPosition;
424 WebPluginController *pluginController;
427 NSToolTipTag lastToolTipTag;
428 id trackingRectOwner;
429 void *trackingRectUserData;
431 NSTimer *autoscrollTimer;
432 NSEvent *autoscrollTriggerEvent;
436 NSMutableDictionary *highlighters;
438 #ifdef BUILDING_ON_TIGER
439 BOOL nextResponderDisabledOnce;
442 WebTextCompleteController *compController;
444 BOOL transparentBackground;
446 WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters;
449 WebDataSource *dataSource;
450 WebCore::CachedImage* promisedDragTIFFDataSource;
452 CFRunLoopTimerRef updateFocusedAndActiveStateTimer;
453 CFRunLoopTimerRef updateMouseoverTimer;
455 SEL selectorForDoCommandBySelector;
458 BOOL enumeratingSubviews;
464 static NSCellStateValue kit(TriState state)
474 ASSERT_NOT_REACHED();
478 @implementation WebHTMLViewPrivate
482 JSC::initializeThreading();
483 #ifndef BUILDING_ON_TIGER
484 WebCoreObjCFinalizeOnMainThread(self);
487 if (!oldSetCursorIMP) {
488 #ifdef BUILDING_ON_TIGER
489 Method setCursorMethod = class_getInstanceMethod([NSCursor class], @selector(set));
491 Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
493 ASSERT(setCursorMethod);
495 oldSetCursorIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
496 ASSERT(oldSetCursorIMP);
499 #ifdef BUILDING_ON_TIGER
500 if (!oldResetCursorRectsIMP) {
501 Method resetCursorRectsMethod = class_getInstanceMethod([NSWindow class], @selector(resetCursorRects));
502 ASSERT(resetCursorRectsMethod);
503 oldResetCursorRectsIMP = method_setImplementation(resetCursorRectsMethod, (IMP)resetCursorRects);
504 ASSERT(oldResetCursorRectsIMP);
512 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self))
515 ASSERT(!autoscrollTimer);
516 ASSERT(!autoscrollTriggerEvent);
517 ASSERT(!updateFocusedAndActiveStateTimer);
518 ASSERT(!updateMouseoverTimer);
520 [mouseDownEvent release];
521 [keyDownEvent release];
522 [pluginController release];
524 [compController release];
525 [dataSource release];
526 [highlighters release];
527 if (promisedDragTIFFDataSource)
528 promisedDragTIFFDataSource->removeClient(promisedDataClient());
535 ASSERT_MAIN_THREAD();
537 if (promisedDragTIFFDataSource)
538 promisedDragTIFFDataSource->removeClient(promisedDataClient());
545 [mouseDownEvent release];
546 [keyDownEvent release];
547 [pluginController release];
549 [compController release];
550 [dataSource release];
551 [highlighters release];
552 if (promisedDragTIFFDataSource)
553 promisedDragTIFFDataSource->removeClient(promisedDataClient());
555 mouseDownEvent = nil;
557 pluginController = nil;
559 compController = nil;
562 promisedDragTIFFDataSource = 0;
564 #if USE(ACCELERATED_COMPOSITING)
565 layerHostingView = nil;
571 @implementation WebHTMLView (WebHTMLViewFileInternal)
573 - (DOMRange *)_documentRange
575 return [[[self _frame] DOMDocument] _documentRange];
578 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
580 NSEnumerator *enumerator = [paths objectEnumerator];
583 while ((path = [enumerator nextObject]) != nil) {
584 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
585 if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
592 - (WebDataSource *)_dataSource
594 return _private->dataSource;
597 - (WebView *)_webView
599 return [_private->dataSource _webView];
602 - (WebFrameView *)_frameView
604 return [[_private->dataSource webFrame] frameView];
607 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
609 DOMDocumentFragment *fragment;
610 NSEnumerator *enumerator = [paths objectEnumerator];
611 NSMutableArray *domNodes = [[NSMutableArray alloc] init];
614 while ((path = [enumerator nextObject]) != nil) {
615 // Non-image file types; _web_userVisibleString is appropriate here because this will
616 // be pasted as visible text.
617 NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
618 [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
621 fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes];
625 return [fragment firstChild] != nil ? fragment : nil;
628 + (NSArray *)_excludedElementsForAttributedStringConversion
630 static NSArray *elements = nil;
631 if (elements == nil) {
632 elements = [[NSArray alloc] initWithObjects:
633 // Omit style since we want style to be inline so the fragment can be easily inserted.
635 // Omit xml so the result is not XHTML.
637 // Omit tags that will get stripped when converted to a fragment anyway.
638 @"doctype", @"html", @"head", @"body",
639 // Omit deprecated tags.
640 @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
641 // Omit object so no file attachments are part of the fragment.
648 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
650 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
651 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
653 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
654 CFRelease(UUIDString);
659 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
660 inContext:(DOMRange *)context
661 allowPlainText:(BOOL)allowPlainText
663 NSArray *types = [pasteboard types];
664 DOMDocumentFragment *fragment = nil;
666 if ([types containsObject:WebArchivePboardType] &&
667 (fragment = [self _documentFragmentFromPasteboard:pasteboard
668 forType:WebArchivePboardType
673 if ([types containsObject:NSFilenamesPboardType] &&
674 (fragment = [self _documentFragmentFromPasteboard:pasteboard
675 forType:NSFilenamesPboardType
680 if ([types containsObject:NSHTMLPboardType] &&
681 (fragment = [self _documentFragmentFromPasteboard:pasteboard
682 forType:NSHTMLPboardType
687 if ([types containsObject:NSRTFPboardType] &&
688 (fragment = [self _documentFragmentFromPasteboard:pasteboard
689 forType:NSRTFPboardType
694 if ([types containsObject:NSRTFDPboardType] &&
695 (fragment = [self _documentFragmentFromPasteboard:pasteboard
696 forType:NSRTFDPboardType
701 if ([types containsObject:NSTIFFPboardType] &&
702 (fragment = [self _documentFragmentFromPasteboard:pasteboard
703 forType:NSTIFFPboardType
708 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
709 if ([types containsObject:NSPICTPboardType] &&
710 (fragment = [self _documentFragmentFromPasteboard:pasteboard
711 forType:NSPICTPboardType
717 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
718 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
719 if ([types containsObject:(NSString*)kUTTypePNG] &&
720 (fragment = [self _documentFragmentFromPasteboard:pasteboard
721 forType:(NSString*)kUTTypePNG
726 if ([types containsObject:NSURLPboardType] &&
727 (fragment = [self _documentFragmentFromPasteboard:pasteboard
728 forType:NSURLPboardType
733 if (allowPlainText && [types containsObject:NSStringPboardType] &&
734 (fragment = [self _documentFragmentFromPasteboard:pasteboard
735 forType:NSStringPboardType
744 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
746 NSArray *types = [pasteboard types];
748 if ([types containsObject:NSStringPboardType])
749 return [pasteboard stringForType:NSStringPboardType];
751 NSAttributedString *attributedString = nil;
754 if ([types containsObject:NSRTFDPboardType])
755 attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
756 if (attributedString == nil && [types containsObject:NSRTFPboardType])
757 attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
758 if (attributedString != nil) {
759 string = [[attributedString string] copy];
760 [attributedString release];
761 return [string autorelease];
764 if ([types containsObject:NSFilenamesPboardType]) {
765 string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
772 if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
773 string = [URL _web_userVisibleString];
774 if ([string length] > 0)
781 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
783 DOMRange *range = [self _selectedRange];
784 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard
785 inContext:range allowPlainText:allowPlainText];
786 WebFrame *frame = [self _frame];
787 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[self _selectedRange] givenAction:WebViewInsertActionPasted]) {
788 [frame _replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:NO];
792 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
794 NSString *text = [self _plainTextFromPasteboard:pasteboard];
795 if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
796 [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
799 - (void)_removeMouseMovedObserverUnconditionally
801 if (!_private || !_private->observingMouseMovedNotifications)
804 [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil];
805 _private->observingMouseMovedNotifications = false;
808 - (void)_removeSuperviewObservers
810 if (!_private || !_private->observingSuperviewNotifications)
813 NSView *superview = [self superview];
814 if (!superview || ![self window])
817 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
818 [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview];
819 [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview];
821 _private->observingSuperviewNotifications = false;
824 - (void)_removeWindowObservers
826 if (!_private->observingWindowNotifications)
829 NSWindow *window = [self window];
833 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
834 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
835 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
836 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window];
837 [notificationCenter removeObserver:self name:WKWindowWillOrderOnScreenNotification() object:window];
839 _private->observingWindowNotifications = false;
842 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
844 WebView *webView = [self _webView];
845 DOMNode *child = [fragment firstChild];
846 if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
847 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
848 return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
851 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
853 WebView *webView = [self _webView];
854 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
857 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
859 return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
862 // Calculate the vertical size of the view that fits on a single page
863 - (float)_calculatePrintHeight
865 // Obtain the print info object for the current operation
866 NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
868 // Calculate the page height in points
869 NSSize paperSize = [pi paperSize];
870 return paperSize.height - [pi topMargin] - [pi bottomMargin];
873 - (DOMRange *)_selectedRange
875 Frame* coreFrame = core([self _frame]);
876 return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil;
879 - (BOOL)_shouldDeleteRange:(DOMRange *)range
881 Frame* coreFrame = core([self _frame]);
882 return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
885 - (NSView *)_hitViewForEvent:(NSEvent *)event
887 // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.
888 // Callers of this method, however, want to query the deepest view instead.
889 forceNSViewHitTest = YES;
890 NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
891 forceNSViewHitTest = NO;
895 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
897 // Put HTML on the pasteboard.
898 if ([types containsObject:WebArchivePboardType]) {
899 if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) {
900 if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0)
901 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType];
905 // Put the attributed string on the pasteboard (RTF/RTFD format).
906 if ([types containsObject:NSRTFDPboardType]) {
907 if (attributedString == nil) {
908 attributedString = [self selectedAttributedString];
910 NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
911 [pasteboard setData:RTFDData forType:NSRTFDPboardType];
913 if ([types containsObject:NSRTFPboardType]) {
914 if (attributedString == nil) {
915 attributedString = [self selectedAttributedString];
917 if ([attributedString containsAttachments]) {
918 attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
920 NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
921 [pasteboard setData:RTFData forType:NSRTFPboardType];
924 // Put plain string on the pasteboard.
925 if ([types containsObject:NSStringPboardType]) {
926 // Map to a plain old space because this is better for source code, other browsers do it,
927 // and because HTML forces you to do this any time you want two spaces in a row.
928 NSMutableString *s = [[self selectedString] mutableCopy];
929 const unichar NonBreakingSpaceCharacter = 0xA0;
930 NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
931 [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
932 [pasteboard setString:s forType:NSStringPboardType];
936 if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
937 [pasteboard setData:nil forType:WebSmartPastePboardType];
941 - (void)_setMouseDownEvent:(NSEvent *)event
943 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
945 if (event == _private->mouseDownEvent)
949 [_private->mouseDownEvent release];
950 _private->mouseDownEvent = event;
953 - (void)_cancelUpdateFocusedAndActiveStateTimer
955 if (_private->updateFocusedAndActiveStateTimer) {
956 CFRunLoopTimerInvalidate(_private->updateFocusedAndActiveStateTimer);
957 CFRelease(_private->updateFocusedAndActiveStateTimer);
958 _private->updateFocusedAndActiveStateTimer = NULL;
962 - (void)_cancelUpdateMouseoverTimer
964 if (_private->updateMouseoverTimer) {
965 CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
966 CFRelease(_private->updateMouseoverTimer);
967 _private->updateMouseoverTimer = NULL;
971 - (WebHTMLView *)_topHTMLView
973 // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
974 WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
976 ASSERT([view isKindOfClass:[WebHTMLView class]]);
980 - (BOOL)_isTopHTMLView
982 // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
983 return self == [self _topHTMLView];
986 - (void)_web_setPrintingModeRecursive
988 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
991 _private->enumeratingSubviews = YES;
994 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
996 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
998 unsigned count = [descendantWebHTMLViews count];
999 for (unsigned i = 0; i < count; ++i)
1000 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
1002 [descendantWebHTMLViews release];
1005 _private->enumeratingSubviews = NO;
1009 - (void)_web_clearPrintingModeRecursive
1011 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
1014 _private->enumeratingSubviews = YES;
1017 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1019 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1021 unsigned count = [descendantWebHTMLViews count];
1022 for (unsigned i = 0; i < count; ++i)
1023 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
1025 [descendantWebHTMLViews release];
1028 _private->enumeratingSubviews = NO;
1032 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize
1034 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
1037 _private->enumeratingSubviews = YES;
1040 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1042 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1044 unsigned count = [descendantWebHTMLViews count];
1045 for (unsigned i = 0; i < count; ++i)
1046 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
1048 [descendantWebHTMLViews release];
1051 _private->enumeratingSubviews = NO;
1057 @implementation WebHTMLView (WebPrivate)
1059 + (NSArray *)supportedMIMETypes
1061 return [WebHTMLRepresentation supportedMIMETypes];
1064 + (NSArray *)supportedImageMIMETypes
1066 return [WebHTMLRepresentation supportedImageMIMETypes];
1069 + (NSArray *)supportedNonImageMIMETypes
1071 return [WebHTMLRepresentation supportedNonImageMIMETypes];
1074 + (NSArray *)unsupportedTextMIMETypes
1076 return [NSArray arrayWithObjects:
1077 @"text/calendar", // iCal
1079 @"text/x-vcalendar",
1081 @"text/vcard", // vCard
1084 @"text/ldif", // Netscape Address Book
1085 @"text/qif", // Quicken
1087 @"text/x-csv", // CSV (for Address Book and Microsoft Outlook)
1088 @"text/x-vcf", // vCard type used in Sun affinity app
1089 @"text/rtf", // Rich Text Format
1093 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
1095 // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged
1096 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1097 location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
1098 modifierFlags:[flagsChangedEvent modifierFlags]
1099 timestamp:[flagsChangedEvent timestamp]
1100 windowNumber:[flagsChangedEvent windowNumber]
1101 context:[flagsChangedEvent context]
1102 eventNumber:0 clickCount:0 pressure:0];
1104 // Pretend it's a mouse move.
1105 [[NSNotificationCenter defaultCenter]
1106 postNotificationName:WKMouseMovedNotification() object:self
1107 userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
1112 // This method exists to maintain compatibility with Leopard's Dictionary.app, since it
1113 // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:.
1114 // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160>
1115 return [self _frame];
1118 - (void)_updateMouseoverWithFakeEvent
1120 [self _cancelUpdateMouseoverTimer];
1122 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1123 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1124 modifierFlags:[[NSApp currentEvent] modifierFlags]
1125 timestamp:[NSDate timeIntervalSinceReferenceDate]
1126 windowNumber:[[self window] windowNumber]
1127 context:[[NSApp currentEvent] context]
1128 eventNumber:0 clickCount:0 pressure:0];
1130 [self _updateMouseoverWithEvent:fakeEvent];
1133 static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
1135 WebHTMLView *view = (WebHTMLView *)info;
1137 [view _updateMouseoverWithFakeEvent];
1140 - (void)_frameOrBoundsChanged
1142 if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
1143 [self setNeedsLayout:YES];
1144 [self setNeedsDisplay:YES];
1145 [_private->compController endRevertingChange:NO moveLeft:NO];
1148 NSPoint origin = [[self superview] bounds].origin;
1149 if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
1150 if (Frame* coreFrame = core([self _frame]))
1151 coreFrame->eventHandler()->sendScrollEvent();
1152 [_private->compController endRevertingChange:NO moveLeft:NO];
1154 WebView *webView = [self _webView];
1155 [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
1157 _private->lastScrollPosition = origin;
1159 if ([self window] && !_private->closed && !_private->updateMouseoverTimer) {
1160 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
1162 // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading
1163 // and scrolling rapidly back to back.
1164 _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0,
1165 _updateMouseoverTimerCallback, &context);
1166 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode);
1170 - (void)_setAsideSubviews
1172 ASSERT(!_private->subviewsSetAside);
1173 ASSERT(_private->savedSubviews == nil);
1174 _private->savedSubviews = _subviews;
1175 #if USE(ACCELERATED_COMPOSITING)
1176 // We need to keep the layer-hosting view in the subviews, otherwise the layers flash.
1177 if (_private->layerHostingView) {
1178 NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil];
1179 _subviews = newSubviews;
1185 _private->subviewsSetAside = YES;
1188 - (void)_restoreSubviews
1190 ASSERT(_private->subviewsSetAside);
1191 #if USE(ACCELERATED_COMPOSITING)
1192 if (_private->layerHostingView) {
1193 [_subviews release];
1194 _subviews = _private->savedSubviews;
1196 ASSERT(_subviews == nil);
1197 _subviews = _private->savedSubviews;
1200 ASSERT(_subviews == nil);
1201 _subviews = _private->savedSubviews;
1203 _private->savedSubviews = nil;
1204 _private->subviewsSetAside = NO;
1209 - (void)didAddSubview:(NSView *)subview
1211 if (_private->enumeratingSubviews)
1212 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]));
1215 - (void)willRemoveSubview:(NSView *)subview
1217 // Have to null-check _private, since this can be called via -dealloc when
1218 // cleaning up the the layerHostingView.
1219 if (_private && _private->enumeratingSubviews)
1220 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]));
1225 #ifdef BUILDING_ON_TIGER
1227 // This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
1228 // That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty
1229 // before doing it. As a compromise, when we're opaque we do the layout only when actually asked to
1230 // draw, but when we're transparent we do the layout at this stage so views behind us know that they
1231 // need to be redrawn (in case the layout causes some things to get dirtied).
1232 - (void)_propagateDirtyRectsToOpaqueAncestors
1234 if (![[self _webView] drawsBackground])
1235 [self _web_layoutIfNeededRecursive];
1236 [super _propagateDirtyRectsToOpaqueAncestors];
1241 - (void)viewWillDraw
1243 // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1244 // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1245 // once the FIXME in _isTopHTMLView is fixed.
1246 if (_private->dataSource && [self _isTopHTMLView])
1247 [self _web_layoutIfNeededRecursive];
1248 [super viewWillDraw];
1253 // Don't let AppKit even draw subviews. We take care of that.
1254 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1256 // This helps when we print as part of a larger print process.
1257 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1258 BOOL wasInPrintingMode = _private->printing;
1259 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1261 if (!wasInPrintingMode)
1262 [self _web_setPrintingModeRecursive];
1263 #ifndef BUILDING_ON_TIGER
1265 [self _web_layoutIfNeededRecursive];
1267 } else if (wasInPrintingMode)
1268 [self _web_clearPrintingModeRecursive];
1270 #ifdef BUILDING_ON_TIGER
1272 // Because Tiger does not have viewWillDraw we need to do layout here.
1273 [self _web_layoutIfNeededRecursive];
1274 [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
1278 [self _setAsideSubviews];
1279 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1280 [self _restoreSubviews];
1282 if (wasInPrintingMode != isPrinting) {
1283 if (wasInPrintingMode)
1284 [self _web_setPrintingModeRecursive];
1286 [self _web_clearPrintingModeRecursive];
1290 // Don't let AppKit even draw subviews. We take care of that.
1291 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1293 BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1295 BOOL wasInPrintingMode = _private->printing;
1296 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1298 if (needToSetAsideSubviews) {
1299 // This helps when we print as part of a larger print process.
1300 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1302 if (!wasInPrintingMode)
1303 [self _web_setPrintingModeRecursive];
1304 #ifndef BUILDING_ON_TIGER
1306 [self _web_layoutIfNeededRecursive];
1308 } else if (wasInPrintingMode)
1309 [self _web_clearPrintingModeRecursive];
1311 #ifdef BUILDING_ON_TIGER
1313 // Because Tiger does not have viewWillDraw we need to do layout here.
1314 NSRect boundsBeforeLayout = [self bounds];
1315 if (!NSIsEmptyRect(visRect))
1316 [self _web_layoutIfNeededRecursive];
1318 // If layout changes the view's bounds, then we need to recompute the visRect.
1319 // That's because the visRect passed to us was based on the bounds at the time
1320 // we were called. This method is only displayed to draw "all", so it's safe
1321 // to just call visibleRect to compute the entire rectangle.
1322 if (!NSEqualRects(boundsBeforeLayout, [self bounds]))
1323 visRect = [self visibleRect];
1327 [self _setAsideSubviews];
1330 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1332 if (needToSetAsideSubviews) {
1333 if (wasInPrintingMode != isPrinting) {
1334 if (wasInPrintingMode)
1335 [self _web_setPrintingModeRecursive];
1337 [self _web_clearPrintingModeRecursive];
1340 [self _restoreSubviews];
1344 // Don't let AppKit even draw subviews. We take care of that.
1345 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView
1347 #ifdef BUILDING_ON_TIGER
1348 // Because Tiger does not have viewWillDraw we need to do layout here.
1349 [self _web_layoutIfNeededRecursive];
1352 [self _setAsideSubviews];
1353 [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView];
1354 [self _restoreSubviews];
1357 - (BOOL)_insideAnotherHTMLView
1359 return self != [self _topHTMLView];
1362 - (NSView *)hitTest:(NSPoint)point
1364 // WebHTMLView objects handle all events for objects inside them.
1365 // To get those events, we prevent hit testing from AppKit.
1367 // But there are three exceptions to this:
1368 // 1) For right mouse clicks and control clicks we don't yet have an implementation
1369 // that works for nested views, so we let the hit testing go through the
1370 // standard NSView code path (needs to be fixed, see bug 4361618).
1371 // 2) Java depends on doing a hit test inside it's mouse moved handling,
1372 // so we let the hit testing go through the standard NSView code path
1373 // when the current event is a mouse move (except when we are calling
1374 // from _updateMouseoverWithEvent, so we have to use a global,
1375 // forceWebHTMLViewHitTest, for that)
1376 // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1377 // both need to figure out which view to check with inside the WebHTMLView.
1378 // They use a global to change the behavior of hitTest: so they can get the
1379 // right view. The global is forceNSViewHitTest and the method they use to
1380 // do the hit testing is _hitViewForEvent:. (But this does not work correctly
1381 // when there is HTML overlapping the view, see bug 4361626)
1382 // 4) NSAccessibilityHitTest relies on this for checking the cursor position.
1383 // Our check for that is whether the event is NSFlagsChanged. This works
1384 // for VoiceOver's cntl-opt-f5 command (move focus to item under cursor)
1385 // and Dictionary's cmd-cntl-D (open dictionary popup for item under cursor).
1386 // This is of course a hack.
1388 BOOL captureHitsOnSubviews;
1389 if (forceNSViewHitTest)
1390 captureHitsOnSubviews = NO;
1391 else if (forceWebHTMLViewHitTest)
1392 captureHitsOnSubviews = YES;
1394 NSEvent *event = [[self window] currentEvent];
1395 captureHitsOnSubviews = !([event type] == NSMouseMoved
1396 || [event type] == NSRightMouseDown
1397 || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1398 || [event type] == NSFlagsChanged);
1401 if (!captureHitsOnSubviews)
1402 return [super hitTest:point];
1403 if ([[self superview] mouse:point inRect:[self frame]])
1408 - (void)_clearLastHitViewIfSelf
1410 if (lastHitView == self)
1414 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1416 ASSERT(_private->trackingRectOwner == nil);
1417 _private->trackingRectOwner = owner;
1418 _private->trackingRectUserData = data;
1419 return TRACKING_RECT_TAG;
1422 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1424 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1425 ASSERT(_private->trackingRectOwner == nil);
1426 _private->trackingRectOwner = owner;
1427 _private->trackingRectUserData = data;
1428 return TRACKING_RECT_TAG;
1431 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1434 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1435 ASSERT(_private->trackingRectOwner == nil);
1436 _private->trackingRectOwner = owner;
1437 _private->trackingRectUserData = userDataList[0];
1438 trackingNums[0] = TRACKING_RECT_TAG;
1441 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1446 if (_private && (tag == TRACKING_RECT_TAG)) {
1447 _private->trackingRectOwner = nil;
1451 if (_private && (tag == _private->lastToolTipTag)) {
1452 [super removeTrackingRect:tag];
1453 _private->lastToolTipTag = 0;
1457 // If any other tracking rect is being removed, we don't know how it was created
1458 // and it's possible there's a leak involved (see 3500217)
1459 ASSERT_NOT_REACHED();
1462 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1465 for (i = 0; i < count; ++i) {
1469 ASSERT(tag == TRACKING_RECT_TAG);
1470 if (_private != nil) {
1471 _private->trackingRectOwner = nil;
1476 - (void)_sendToolTipMouseExited
1478 // Nothing matters except window, trackingNumber, and userData.
1479 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1480 location:NSMakePoint(0, 0)
1483 windowNumber:[[self window] windowNumber]
1486 trackingNumber:TRACKING_RECT_TAG
1487 userData:_private->trackingRectUserData];
1488 [_private->trackingRectOwner mouseExited:fakeEvent];
1491 - (void)_sendToolTipMouseEntered
1493 // Nothing matters except window, trackingNumber, and userData.
1494 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1495 location:NSMakePoint(0, 0)
1498 windowNumber:[[self window] windowNumber]
1501 trackingNumber:TRACKING_RECT_TAG
1502 userData:_private->trackingRectUserData];
1503 [_private->trackingRectOwner mouseEntered:fakeEvent];
1506 - (void)_setToolTip:(NSString *)string
1508 NSString *toolTip = [string length] == 0 ? nil : string;
1509 NSString *oldToolTip = _private->toolTip;
1510 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1514 [self _sendToolTipMouseExited];
1515 [oldToolTip release];
1517 _private->toolTip = [toolTip copy];
1519 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1520 [self removeAllToolTips];
1521 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1522 _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1523 [self _sendToolTipMouseEntered];
1527 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1529 return [[_private->toolTip copy] autorelease];
1532 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1534 if (_private->closed)
1537 NSView *contentView = [[event window] contentView];
1538 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1540 forceWebHTMLViewHitTest = YES;
1541 NSView *hitView = [contentView hitTest:locationForHitTest];
1542 forceWebHTMLViewHitTest = NO;
1544 WebHTMLView *view = nil;
1545 if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
1546 view = (WebHTMLView *)hitView;
1551 if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1552 // If we are moving out of a view (or frame), let's pretend the mouse moved
1553 // all the way out of that view. But we have to account for scrolling, because
1554 // khtml doesn't understand our clipping.
1555 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1556 float yScroll = visibleRect.origin.y;
1557 float xScroll = visibleRect.origin.x;
1559 event = [NSEvent mouseEventWithType:NSMouseMoved
1560 location:NSMakePoint(-1 - xScroll, -1 - yScroll )
1561 modifierFlags:[[NSApp currentEvent] modifierFlags]
1562 timestamp:[NSDate timeIntervalSinceReferenceDate]
1563 windowNumber:[[view window] windowNumber]
1564 context:[[NSApp currentEvent] context]
1565 eventNumber:0 clickCount:0 pressure:0];
1566 if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1567 lastHitCoreFrame->eventHandler()->mouseMoved(event);
1573 if (Frame* coreFrame = core([view _frame]))
1574 coreFrame->eventHandler()->mouseMoved(event);
1580 // keep in sync with WebPasteboardHelper::insertablePasteboardTypes
1581 + (NSArray *)_insertablePasteboardTypes
1583 static NSArray *types = nil;
1585 types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType,
1586 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
1589 NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
1595 + (NSArray *)_selectionPasteboardTypes
1597 // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1598 return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1601 - (NSImage *)_dragImageForURL:(NSString*)urlString withLabel:(NSString*)label
1603 BOOL drawURLString = YES;
1604 BOOL clipURLString = NO, clipLabelString = NO;
1611 NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
1612 toHaveTrait:NSBoldFontMask];
1613 NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
1615 labelSize.width = [label _web_widthWithFont: labelFont];
1616 labelSize.height = [labelFont ascender] - [labelFont descender];
1617 if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
1618 labelSize.width = MAX_DRAG_LABEL_WIDTH;
1619 clipLabelString = YES;
1622 NSSize imageSize, urlStringSize;
1623 imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2.0f;
1624 imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2.0f;
1625 if (drawURLString) {
1626 urlStringSize.width = [urlString _web_widthWithFont: urlFont];
1627 urlStringSize.height = [urlFont ascender] - [urlFont descender];
1628 imageSize.height += urlStringSize.height;
1629 if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
1630 imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2.0f, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
1631 clipURLString = YES;
1633 imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2.0f, urlStringSize.width + DRAG_LABEL_BORDER_X * 2.0f);
1636 NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
1637 [dragImage lockFocus];
1639 [[NSColor colorWithDeviceRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
1641 // Drag a rectangle with rounded corners/
1642 NSBezierPath *path = [NSBezierPath bezierPath];
1643 [path appendBezierPathWithOvalInRect: NSMakeRect(0.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1644 [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1645 [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)];
1646 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1648 [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0.0f, imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height)];
1649 [path appendBezierPathWithRect: NSMakeRect(0.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1650 [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)];
1653 NSColor *topColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.75f];
1654 NSColor *bottomColor = [NSColor colorWithDeviceWhite:1.0f alpha:0.5f];
1655 if (drawURLString) {
1657 urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:urlFont];
1659 [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender])
1660 withTopColor:topColor bottomColor:bottomColor font:urlFont];
1663 if (clipLabelString)
1664 label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:labelFont];
1665 [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
1666 withTopColor:topColor bottomColor:bottomColor font:labelFont];
1668 [dragImage unlockFocus];
1673 - (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
1675 NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
1677 NSString *label = [element objectForKey: WebElementLinkLabelKey];
1678 NSString *urlString = [linkURL _web_userVisibleString];
1679 return [self _dragImageForURL:urlString withLabel:label];
1682 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1684 [self setPromisedDragTIFFDataSource:0];
1687 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1689 if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1690 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1691 [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1693 } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1694 if (Image* image = [self promisedDragTIFFDataSource]->image())
1695 [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1696 [self setPromisedDragTIFFDataSource:0];
1700 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
1702 [self autoscroll:event];
1703 [self _startAutoscrollTimer:event];
1706 - (WebPluginController *)_pluginController
1708 return _private->pluginController;
1711 - (void)_layoutForPrinting
1713 // Set printing mode temporarily so we can adjust the size of the view. This will allow
1714 // AppKit's pagination code to use the correct height for the page content. Leaving printing
1715 // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1716 // turn it off again after adjusting the size.
1717 [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1718 [self _web_clearPrintingModeRecursive];
1721 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1723 if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
1725 *beforeString = nil;
1731 [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
1734 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
1736 return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
1739 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1741 if (_private->autoscrollTimer == nil) {
1742 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1743 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1744 _private->autoscrollTriggerEvent = [triggerEvent retain];
1748 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1749 // We can't remove this yet because it's still in use by Mail.
1750 - (NSRect)_selectionRect
1752 return [self selectionRect];
1755 - (void)_stopAutoscrollTimer
1757 NSTimer *timer = _private->autoscrollTimer;
1758 _private->autoscrollTimer = nil;
1759 [_private->autoscrollTriggerEvent release];
1760 _private->autoscrollTriggerEvent = nil;
1767 // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1768 // a mouse up event.
1769 BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);
1771 [self _stopAutoscrollTimer];
1775 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1776 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1777 modifierFlags:[[NSApp currentEvent] modifierFlags]
1778 timestamp:[NSDate timeIntervalSinceReferenceDate]
1779 windowNumber:[[self window] windowNumber]
1780 context:[[NSApp currentEvent] context]
1781 eventNumber:0 clickCount:0 pressure:0];
1782 [self mouseDragged:fakeEvent];
1787 Frame* coreFrame = core([self _frame]);
1788 return coreFrame && coreFrame->editor()->canEdit();
1791 - (BOOL)_canEditRichly
1793 Frame* coreFrame = core([self _frame]);
1794 return coreFrame && coreFrame->editor()->canEditRichly();
1797 - (BOOL)_canAlterCurrentSelection
1799 return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1802 - (BOOL)_hasSelection
1804 Frame* coreFrame = core([self _frame]);
1805 return coreFrame && coreFrame->selection()->isRange();
1808 - (BOOL)_hasSelectionOrInsertionPoint
1810 Frame* coreFrame = core([self _frame]);
1811 return coreFrame && coreFrame->selection()->isCaretOrRange();
1814 - (BOOL)_hasInsertionPoint
1816 Frame* coreFrame = core([self _frame]);
1817 return coreFrame && coreFrame->selection()->isCaret();
1822 Frame* coreFrame = core([self _frame]);
1823 return coreFrame && coreFrame->selection()->isContentEditable();
1826 - (BOOL)_transparentBackground
1828 return _private->transparentBackground;
1831 - (void)_setTransparentBackground:(BOOL)f
1833 _private->transparentBackground = f;
1836 - (NSImage *)_selectionDraggingImage
1838 if ([self _hasSelection]) {
1839 NSImage *dragImage = core([self _frame])->selectionImage();
1840 [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1846 - (NSRect)_selectionDraggingRect
1848 // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1849 return [self selectionRect];
1852 - (DOMNode *)_insertOrderedList
1854 Frame* coreFrame = core([self _frame]);
1855 return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
1858 - (DOMNode *)_insertUnorderedList
1860 Frame* coreFrame = core([self _frame]);
1861 return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
1864 - (BOOL)_canIncreaseSelectionListLevel
1866 Frame* coreFrame = core([self _frame]);
1867 return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
1870 - (BOOL)_canDecreaseSelectionListLevel
1872 Frame* coreFrame = core([self _frame]);
1873 return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
1876 - (DOMNode *)_increaseSelectionListLevel
1878 Frame* coreFrame = core([self _frame]);
1879 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
1882 - (DOMNode *)_increaseSelectionListLevelOrdered
1884 Frame* coreFrame = core([self _frame]);
1885 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
1888 - (DOMNode *)_increaseSelectionListLevelUnordered
1890 Frame* coreFrame = core([self _frame]);
1891 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
1894 - (void)_decreaseSelectionListLevel
1896 Frame* coreFrame = core([self _frame]);
1898 coreFrame->editor()->decreaseSelectionListLevel();
1901 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1903 if (!_private->highlighters)
1904 _private->highlighters = [[NSMutableDictionary alloc] init];
1905 [_private->highlighters setObject:highlighter forKey:type];
1908 - (void)_removeHighlighterOfType:(NSString*)type
1910 [_private->highlighters removeObjectForKey:type];
1913 - (void)_updateFocusedAndActiveState
1915 [self _cancelUpdateFocusedAndActiveStateTimer];
1917 [[self _webView] _updateFocusedAndActiveStateForFrame:[self _frame]];
1920 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1922 ASSERT([self _hasSelection]);
1923 NSArray *types = [self pasteboardTypesForSelection];
1925 // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1926 NSAttributedString *attributedString = [self selectedAttributedString];
1927 NSMutableArray *mutableTypes = nil;
1928 if (![attributedString containsAttachments]) {
1929 mutableTypes = [types mutableCopy];
1930 [mutableTypes removeObject:NSRTFDPboardType];
1931 types = mutableTypes;
1934 [pasteboard declareTypes:types owner:[self _topHTMLView]];
1935 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1936 [mutableTypes release];
1941 // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing
1942 // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
1943 if (!_private || _private->closed)
1946 _private->closed = YES;
1948 [self _cancelUpdateMouseoverTimer];
1949 [self _cancelUpdateFocusedAndActiveStateTimer];
1950 [self _clearLastHitViewIfSelf];
1951 [self _removeMouseMovedObserverUnconditionally];
1952 [self _removeWindowObservers];
1953 [self _removeSuperviewObservers];
1954 [_private->pluginController destroyAllPlugins];
1955 [_private->pluginController setDataSource:nil];
1956 // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1957 [self removeAllToolTips];
1959 #if USE(ACCELERATED_COMPOSITING)
1960 if (_private->layerHostingView)
1961 [[self _webView] _stoppedAcceleratedCompositingForFrame:[self _frame]];
1966 Page* page = core([self _webView]);
1968 page->dragController()->setDraggingImageURL(KURL());
1971 - (BOOL)_hasHTMLDocument
1973 Frame* coreFrame = core([self _frame]);
1976 Document* document = coreFrame->document();
1977 return document && document->isHTMLDocument();
1980 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1981 forType:(NSString *)pboardType
1982 inContext:(DOMRange *)context
1983 subresources:(NSArray **)subresources
1985 if (pboardType == WebArchivePboardType) {
1986 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1988 *subresources = [archive subresources];
1989 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1993 if (pboardType == NSFilenamesPboardType)
1994 return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1996 if (pboardType == NSHTMLPboardType) {
1997 NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1998 // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1999 if ([HTMLString hasPrefix:@"Version:"]) {
2000 NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
2001 if (range.location != NSNotFound)
2002 HTMLString = [HTMLString substringFromIndex:range.location];
2004 if ([HTMLString length] == 0)
2007 return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
2010 // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
2011 // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
2012 // FIXME: Remove this once bug 5052369 is fixed.
2013 if ([self _hasHTMLDocument] && pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType) {
2014 NSAttributedString *string = nil;
2015 if (pboardType == NSRTFDPboardType)
2016 string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
2018 string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
2022 NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
2023 [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
2024 self, @"WebResourceHandler", nil];
2027 BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
2028 if (!wasDeferringCallbacks)
2029 [[self _webView] setDefersCallbacks:YES];
2031 DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length])
2032 document:[[self _frame] DOMDocument]
2033 documentAttributes:documentAttributes
2038 NSEnumerator *e = [s objectEnumerator];
2040 while ((r = [e nextObject]))
2041 [[self _dataSource] addSubresource:r];
2043 if (!wasDeferringCallbacks)
2044 [[self _webView] setDefersCallbacks:NO];
2046 [documentAttributes release];
2050 if (pboardType == NSTIFFPboardType) {
2051 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
2052 URL:uniqueURLWithRelativePart(@"image.tiff")
2053 MIMEType:@"image/tiff"
2054 textEncodingName:nil
2056 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2060 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
2061 if (pboardType == NSPICTPboardType) {
2062 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
2063 URL:uniqueURLWithRelativePart(@"image.pict")
2064 MIMEType:@"image/pict"
2065 textEncodingName:nil
2067 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2072 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
2073 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
2074 if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) {
2075 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG]
2076 URL:uniqueURLWithRelativePart(@"image.png")
2077 MIMEType:@"image/png"
2078 textEncodingName:nil
2080 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2084 if (pboardType == NSURLPboardType) {
2085 NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
2086 DOMDocument* document = [[self _frame] DOMDocument];
2090 DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
2091 NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose.
2092 if ([URLString length] == 0)
2094 NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping];
2095 DOMText *text = [document createTextNode:URLTitleString];
2096 [anchor setHref:URLString];
2097 [anchor appendChild:text];
2098 DOMDocumentFragment *fragment = [document createDocumentFragment];
2099 [fragment appendChild:anchor];
2102 if (pboardType == NSStringPboardType)
2103 return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get());
2107 #if ENABLE(NETSCAPE_PLUGIN_API)
2108 - (void)_pauseNullEventsForAllNetscapePlugins
2110 NSArray *subviews = [self subviews];
2111 unsigned int subviewCount = [subviews count];
2112 unsigned int subviewIndex;
2114 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2115 NSView *subview = [subviews objectAtIndex:subviewIndex];
2116 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2117 [(WebBaseNetscapePluginView *)subview stopTimers];
2122 #if ENABLE(NETSCAPE_PLUGIN_API)
2123 - (void)_resumeNullEventsForAllNetscapePlugins
2125 NSArray *subviews = [self subviews];
2126 unsigned int subviewCount = [subviews count];
2127 unsigned int subviewIndex;
2129 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2130 NSView *subview = [subviews objectAtIndex:subviewIndex];
2131 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2132 [(WebBaseNetscapePluginView *)subview restartTimers];
2137 - (BOOL)_isUsingAcceleratedCompositing
2139 #if USE(ACCELERATED_COMPOSITING)
2140 return _private->layerHostingView != nil;
2148 @implementation NSView (WebHTMLViewFileInternal)
2150 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
2152 unsigned count = [_subviews count];
2153 for (unsigned i = 0; i < count; ++i) {
2154 NSView *child = [_subviews objectAtIndex:i];
2155 if ([child isKindOfClass:[WebHTMLView class]])
2156 [array addObject:child];
2157 [child _web_addDescendantWebHTMLViewsToArray:array];
2163 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
2165 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
2167 if (object == nil) {
2168 [self removeObjectForKey:key];
2170 [self setObject:object forKey:key];
2176 @interface NSString (WebHTMLViewFileInternal)
2177 - (BOOL)matchesExtensionEquivalent:(NSString *)extension;
2180 @implementation NSString (WebHTMLViewFileInternal)
2182 - (BOOL)matchesExtensionEquivalent:(NSString *)extension
2184 if ([self hasSuffix:extension])
2186 else if ([extension isEqualToString:@"jpeg"] && [self hasSuffix:@"jpg"])
2193 #ifdef BUILDING_ON_TIGER
2195 // The following is a workaround for
2196 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
2197 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
2198 // Since the category will be searched before the real class, we'll prevent the flag from being
2199 // set on the tool tip panel.
2201 @interface NSToolTipPanel : NSPanel
2204 @interface NSToolTipPanel (WebHTMLViewFileInternal)
2207 @implementation NSToolTipPanel (WebHTMLViewFileInternal)
2209 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
2211 // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
2218 @interface NSArray (WebHTMLView)
2219 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
2222 @implementation WebHTMLView
2226 [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes]
2227 returnTypes:[[self class] _insertablePasteboardTypes]];
2228 JSC::initializeThreading();
2229 #ifndef BUILDING_ON_TIGER
2230 WebCoreObjCFinalizeOnMainThread(self);
2234 - (id)initWithFrame:(NSRect)frame
2236 self = [super initWithFrame:frame];
2240 [self setFocusRingType:NSFocusRingTypeNone];
2242 // Make all drawing go through us instead of subviews.
2243 [self _setDrawsOwnDescendants:YES];
2245 _private = [[WebHTMLViewPrivate alloc] init];
2247 _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2248 _private->needsLayout = YES;
2255 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self))
2258 // We can't assert that close has already been called because
2259 // this view can be removed from it's superview, even though
2260 // it could be needed later, so close if needed.
2269 ASSERT_MAIN_THREAD();
2270 // We can't assert that close has already been called because
2271 // this view can be removed from it's superview, even though
2272 // it could be needed later, so close if needed.
2277 // Returns YES if the delegate returns YES (so we should do no more work).
2278 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2280 BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2281 _private->selectorForDoCommandBySelector = 0;
2282 if (callerAlreadyCalledDelegate)
2284 WebView *webView = [self _webView];
2285 return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2288 static String commandNameForSelector(SEL selector)
2290 // Change a few command names into ones supported by WebCore::Editor.
2291 // If this list gets too long we might decide we need to use a hash table.
2292 if (selector == @selector(insertParagraphSeparator:) || selector == @selector(insertNewlineIgnoringFieldEditor:))
2293 return "InsertNewline";
2294 if (selector == @selector(insertTabIgnoringFieldEditor:))
2296 if (selector == @selector(pageDown:))
2297 return "MovePageDown";
2298 if (selector == @selector(pageDownAndModifySelection:))
2299 return "MovePageDownAndModifySelection";
2300 if (selector == @selector(pageUp:))
2301 return "MovePageUp";
2302 if (selector == @selector(pageUpAndModifySelection:))
2303 return "MovePageUpAndModifySelection";
2305 // Remove the trailing colon.
2306 const char* selectorName = sel_getName(selector);
2307 size_t selectorNameLength = strlen(selectorName);
2308 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2310 return String(selectorName, selectorNameLength - 1);
2313 - (Editor::Command)coreCommandBySelector:(SEL)selector
2315 Frame* coreFrame = core([self _frame]);
2317 return Editor::Command();
2318 return coreFrame->editor()->command(commandNameForSelector(selector));
2321 - (Editor::Command)coreCommandByName:(const char*)name
2323 Frame* coreFrame = core([self _frame]);
2325 return Editor::Command();
2326 return coreFrame->editor()->command(name);
2329 - (void)executeCoreCommandBySelector:(SEL)selector
2331 if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2333 [self coreCommandBySelector:selector].execute();
2336 - (void)executeCoreCommandByName:(const char*)name
2338 [self coreCommandByName:name].execute();
2341 // These commands are forwarded to the Editor object in WebCore.
2342 // Ideally we'd do this for all editing commands; more of the code
2343 // should be moved from here to there, and more commands should be
2344 // added to this list.
2346 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2347 // The functions are identical.
2349 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2351 WEBCORE_COMMAND(alignCenter)
2352 WEBCORE_COMMAND(alignJustified)
2353 WEBCORE_COMMAND(alignLeft)
2354 WEBCORE_COMMAND(alignRight)
2355 WEBCORE_COMMAND(copy)
2356 WEBCORE_COMMAND(cut)
2357 WEBCORE_COMMAND(delete)
2358 WEBCORE_COMMAND(deleteBackward)
2359 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2360 WEBCORE_COMMAND(deleteForward)
2361 WEBCORE_COMMAND(deleteToBeginningOfLine)
2362 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2363 WEBCORE_COMMAND(deleteToEndOfLine)
2364 WEBCORE_COMMAND(deleteToEndOfParagraph)
2365 WEBCORE_COMMAND(deleteToMark)
2366 WEBCORE_COMMAND(deleteWordBackward)
2367 WEBCORE_COMMAND(deleteWordForward)
2368 WEBCORE_COMMAND(ignoreSpelling)
2369 WEBCORE_COMMAND(indent)
2370 WEBCORE_COMMAND(insertBacktab)
2371 WEBCORE_COMMAND(insertLineBreak)
2372 WEBCORE_COMMAND(insertNewline)
2373 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2374 WEBCORE_COMMAND(insertParagraphSeparator)
2375 WEBCORE_COMMAND(insertTab)
2376 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2377 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
2378 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
2379 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
2380 WEBCORE_COMMAND(moveBackward)
2381 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2382 WEBCORE_COMMAND(moveDown)
2383 WEBCORE_COMMAND(moveDownAndModifySelection)
2384 WEBCORE_COMMAND(moveForward)
2385 WEBCORE_COMMAND(moveForwardAndModifySelection)
2386 WEBCORE_COMMAND(moveLeft)
2387 WEBCORE_COMMAND(moveLeftAndModifySelection)
2388 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2389 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2390 WEBCORE_COMMAND(moveRight)
2391 WEBCORE_COMMAND(moveRightAndModifySelection)
2392 WEBCORE_COMMAND(moveToBeginningOfDocument)
2393 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2394 WEBCORE_COMMAND(moveToBeginningOfLine)
2395 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2396 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2397 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2398 WEBCORE_COMMAND(moveToBeginningOfSentence)
2399 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2400 WEBCORE_COMMAND(moveToEndOfDocument)
2401 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2402 WEBCORE_COMMAND(moveToEndOfLine)
2403 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2404 WEBCORE_COMMAND(moveToEndOfParagraph)
2405 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2406 WEBCORE_COMMAND(moveToEndOfSentence)
2407 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2408 WEBCORE_COMMAND(moveToLeftEndOfLine)
2409 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
2410 WEBCORE_COMMAND(moveToRightEndOfLine)
2411 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
2412 WEBCORE_COMMAND(moveUp)
2413 WEBCORE_COMMAND(moveUpAndModifySelection)
2414 WEBCORE_COMMAND(moveWordBackward)
2415 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2416 WEBCORE_COMMAND(moveWordForward)
2417 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2418 WEBCORE_COMMAND(moveWordLeft)
2419 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2420 WEBCORE_COMMAND(moveWordRight)
2421 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2422 WEBCORE_COMMAND(outdent)
2423 WEBCORE_COMMAND(pageDown)
2424 WEBCORE_COMMAND(pageDownAndModifySelection)
2425 WEBCORE_COMMAND(pageUp)
2426 WEBCORE_COMMAND(pageUpAndModifySelection)
2427 WEBCORE_COMMAND(selectAll)
2428 WEBCORE_COMMAND(selectLine)
2429 WEBCORE_COMMAND(selectParagraph)
2430 WEBCORE_COMMAND(selectSentence)
2431 WEBCORE_COMMAND(selectToMark)
2432 WEBCORE_COMMAND(selectWord)
2433 WEBCORE_COMMAND(setMark)
2434 WEBCORE_COMMAND(subscript)
2435 WEBCORE_COMMAND(superscript)
2436 WEBCORE_COMMAND(swapWithMark)
2437 WEBCORE_COMMAND(transpose)
2438 WEBCORE_COMMAND(underline)
2439 WEBCORE_COMMAND(unscript)
2440 WEBCORE_COMMAND(yank)
2441 WEBCORE_COMMAND(yankAndSelect)
2443 #undef WEBCORE_COMMAND
2445 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2447 - (IBAction)takeFindStringFromSelection:(id)sender
2451 if (![self _hasSelection]) {
2456 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2459 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2461 [pasteboard declareTypes:types owner:[self _topHTMLView]];
2462 [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2466 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2468 Frame* coreFrame = core([self _frame]);
2471 if (coreFrame->selection()->isContentRichlyEditable())
2472 [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
2474 [self _pasteAsPlainTextWithPasteboard:pasteboard];
2478 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2480 BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]);
2481 BOOL isReturnTypeOK = !returnType || ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]);
2482 if (isSendTypeOK && isReturnTypeOK)
2484 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2487 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2488 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2489 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2490 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2491 // might be using the selector, and we don't want to break them.
2492 - (void)jumpToSelection:(id)sender
2496 if (Frame* coreFrame = core([self _frame]))
2497 coreFrame->revealSelection(ScrollAlignment::alignCenterAlways);
2500 - (NSCellStateValue)selectionHasStyle:(CSSStyleDeclaration*)style
2502 Frame* coreFrame = core([self _frame]);
2505 return kit(coreFrame->editor()->selectionHasStyle(style));
2508 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2510 SEL action = [item action];
2511 RefPtr<Frame> frame = core([self _frame]);
2516 if (Document* doc = frame->document()) {
2517 if (doc->isPluginDocument())
2519 if (doc->isImageDocument()) {
2520 if (action == @selector(copy:))
2521 return frame->loader()->isComplete();
2526 if (action == @selector(changeSpelling:)
2527 || action == @selector(_changeSpellingFromMenu:)
2528 || action == @selector(checkSpelling:)
2529 || action == @selector(complete:)
2530 || action == @selector(pasteFont:))
2531 return [self _canEdit];
2533 if (action == @selector(showGuessPanel:)) {
2534 #ifndef BUILDING_ON_TIGER
2535 // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2536 NSMenuItem *menuItem = (NSMenuItem *)item;
2537 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2538 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2539 [menuItem setTitle:panelShowing
2540 ? UI_STRING("Hide Spelling and Grammar", "menu item title")
2541 : UI_STRING("Show Spelling and Grammar", "menu item title")];
2544 return [self _canEdit];
2547 if (action == @selector(changeBaseWritingDirection:)
2548 || action == @selector(makeBaseWritingDirectionLeftToRight:)
2549 || action == @selector(makeBaseWritingDirectionRightToLeft:)) {
2550 NSWritingDirection writingDirection;
2552 if (action == @selector(changeBaseWritingDirection:)) {
2553 writingDirection = static_cast<NSWritingDirection>([item tag]);
2554 if (writingDirection == NSWritingDirectionNatural)
2556 } else if (action == @selector(makeBaseWritingDirectionLeftToRight:))
2557 writingDirection = NSWritingDirectionLeftToRight;
2559 writingDirection = NSWritingDirectionRightToLeft;
2561 NSMenuItem *menuItem = (NSMenuItem *)item;
2562 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2563 RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create();
2565 style->setProperty("direction", writingDirection == NSWritingDirectionLeftToRight ? "LTR" : "RTL", ec);
2566 [menuItem setState:frame->editor()->selectionHasStyle(style.get())];
2568 return [self _canEdit];
2571 if (action == @selector(makeBaseWritingDirectionNatural:)) {
2572 NSMenuItem *menuItem = (NSMenuItem *)item;
2573 if ([menuItem isKindOfClass:[NSMenuItem class]])
2574 [menuItem setState:NSOffState];
2578 if (action == @selector(toggleBaseWritingDirection:)) {
2579 NSMenuItem *menuItem = (NSMenuItem *)item;
2580 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2581 RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create();
2583 style->setProperty("direction", "RTL", ec);
2584 // Take control of the title of the menu item instead of just checking/unchecking it because
2585 // a check would be ambiguous.
2586 [menuItem setTitle:frame->editor()->selectionHasStyle(style.get())
2587 ? UI_STRING("Left to Right", "Left to Right context menu item")
2588 : UI_STRING("Right to Left", "Right to Left context menu item")];
2590 return [self _canEdit];
2593 if (action == @selector(changeAttributes:)
2594 || action == @selector(changeColor:)
2595 || action == @selector(changeFont:))
2596 return [self _canEditRichly];
2598 if (action == @selector(capitalizeWord:)
2599 || action == @selector(lowercaseWord:)
2600 || action == @selector(uppercaseWord:))
2601 return [self _hasSelection] && [self _isEditable];
2603 if (action == @selector(centerSelectionInVisibleArea:)
2604 || action == @selector(jumpToSelection:)
2605 || action == @selector(copyFont:))
2606 return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2608 if (action == @selector(changeDocumentBackgroundColor:))
2609 return [[self _webView] isEditable] && [self _canEditRichly];
2611 if (action == @selector(_ignoreSpellingFromMenu:)
2612 || action == @selector(_learnSpellingFromMenu:)
2613 || action == @selector(takeFindStringFromSelection:))
2614 return [self _hasSelection];
2616 if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2617 return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste());
2619 if (action == @selector(pasteAsRichText:))
2620 return frame && (frame->editor()->canDHTMLPaste()
2621 || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable()));
2623 if (action == @selector(performFindPanelAction:))
2626 if (action == @selector(_lookUpInDictionaryFromMenu:))
2627 return [self _hasSelection];
2629 #ifndef BUILDING_ON_TIGER
2630 if (action == @selector(toggleGrammarChecking:)) {
2631 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate
2632 // the selector here because we implement it here, and we must implement it here because the AppKit
2633 // code checks the first responder.
2634 NSMenuItem *menuItem = (NSMenuItem *)item;
2635 if ([menuItem isKindOfClass:[NSMenuItem class]])
2636 [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2641 Editor::Command command = [self coreCommandBySelector:action];
2642 if (command.isSupported()) {
2643 NSMenuItem *menuItem = (NSMenuItem *)item;
2644 if ([menuItem isKindOfClass:[NSMenuItem class]])
2645 [menuItem setState:kit(command.state())];
2646 return command.isEnabled();
2652 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2654 // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
2655 // assumes the WebVIew is non-nil.
2656 if (![self _webView])
2658 BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2659 return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2662 - (BOOL)acceptsFirstResponder
2664 // Don't accept first responder when we first click on this view.
2665 // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2666 // Do accept first responder at any other time, for example from keyboard events,
2667 // or from calls back from WebCore once we begin mouse-down event handling.
2668 NSEvent *event = [NSApp currentEvent];
2669 if ([event type] == NSLeftMouseDown
2670 && !_private->handlingMouseDownEvent
2671 && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2677 - (BOOL)maintainsInactiveSelection
2679 // This method helps to determine whether the WebHTMLView should maintain
2680 // an inactive selection when it's not first responder.
2681 // Traditionally, these views have not maintained such selections,
2682 // clearing them when the view was not first responder. However,
2683 // to fix bugs like this one:
2684 // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even
2685 // when they're not firstResponder"
2686 // it was decided to add a switch to act more like an NSTextView.
2688 if ([[self _webView] maintainsInactiveSelection])
2691 // Predict the case where we are losing first responder status only to
2692 // gain it back again. Want to keep the selection in that case.
2693 id nextResponder = [[self window] _newFirstResponderAfterResigning];
2694 if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2695 id contentView = [nextResponder contentView];
2697 nextResponder = contentView;
2699 if ([nextResponder isKindOfClass:[NSClipView class]]) {
2700 id documentView = [nextResponder documentView];
2702 nextResponder = documentView;
2704 if (nextResponder == self)
2707 Frame* coreFrame = core([self _frame]);
2708 bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable();
2709 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2710 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2712 return selectionIsEditable && nextResponderIsInWebView;
2715 - (void)addMouseMovedObserver
2717 if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications)
2720 // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2721 if (!([[self window] isKeyWindow]
2722 #if ENABLE(DASHBOARD_SUPPORT)
2723 || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]
2728 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2729 name:WKMouseMovedNotification() object:nil];
2730 [self _frameOrBoundsChanged];
2731 _private->observingMouseMovedNotifications = true;
2734 - (void)removeMouseMovedObserver
2736 #if ENABLE(DASHBOARD_SUPPORT)
2737 // Don't remove the observer if we're running the Dashboard.
2738 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2742 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2743 [self _removeMouseMovedObserverUnconditionally];
2746 - (void)addSuperviewObservers
2748 // We watch the bounds of our superview, so that we can do a layout when the size
2749 // of the superview changes. This is different from other scrollable things that don't
2750 // need this kind of thing because their layout doesn't change.
2752 // We need to pay attention to both height and width because our "layout" has to change
2753 // to extend the background the full height of the space and because some elements have
2754 // sizes that are based on the total size of the view.
2756 if (_private->observingSuperviewNotifications)
2759 NSView *superview = [self superview];
2760 if (!superview || ![self window])
2763 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2764 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview];
2765 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview];
2767 // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2768 // It will check the current size/scroll against the previous layout's size/scroll. We need to
2769 // do this here to catch the case where the WebView is laid out at one size, removed from its
2770 // window, resized, and inserted into another window. Our frame/bounds changed notifications
2771 // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2772 [self _frameOrBoundsChanged];
2774 _private->observingSuperviewNotifications = true;
2777 - (void)addWindowObservers
2779 if (_private->observingWindowNotifications)
2782 NSWindow *window = [self window];
2786 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2787 [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
2788 [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
2789 [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
2790 [notificationCenter addObserver:self selector:@selector(windowWillOrderOnScreen:) name:WKWindowWillOrderOnScreenNotification() object:window];
2792 _private->observingWindowNotifications = true;
2795 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2797 [self _removeSuperviewObservers];
2800 - (void)viewDidMoveToSuperview
2802 if ([self superview] != nil)
2803 [self addSuperviewObservers];
2806 static void _updateFocusedAndActiveStateTimerCallback(CFRunLoopTimerRef timer, void *info)
2808 WebHTMLView *view = (WebHTMLView *)info;
2809 [view _updateFocusedAndActiveState];
2812 - (void)viewWillMoveToWindow:(NSWindow *)window
2814 // Don't do anything if we aren't initialized. This happens
2815 // when decoding a WebView. When WebViews are decoded their subviews
2816 // are created by initWithCoder: and so won't be normally
2817 // initialized. The stub views are discarded by WebView.
2821 // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2822 [self _removeMouseMovedObserverUnconditionally];
2823 [self _removeWindowObservers];
2824 [self _removeSuperviewObservers];
2825 [self _cancelUpdateMouseoverTimer];
2826 [self _cancelUpdateFocusedAndActiveStateTimer];
2828 [[self _pluginController] stopAllPlugins];
2831 - (void)viewDidMoveToWindow
2833 // Don't do anything if we aren't initialized. This happens
2834 // when decoding a WebView. When WebViews are decoded their subviews
2835 // are created by initWithCoder: and so won't be normally
2836 // initialized. The stub views are discarded by WebView.
2837 if (!_private || _private->closed)
2840 [self _stopAutoscrollTimer];
2841 if ([self window]) {
2842 _private->lastScrollPosition = [[self superview] bounds].origin;
2843 [self addWindowObservers];
2844 [self addSuperviewObservers];
2845 [self addMouseMovedObserver];
2847 // Schedule this update, rather than making the call right now.
2848 // The reason is that placing the caret in the just-installed view requires
2849 // the HTML/XML document to be available on the WebCore side, but it is not
2850 // at the time this code is running. However, it will be there on the next
2851 // crank of the run loop. Doing this helps to make a blinking caret appear
2852 // in a new, empty window "automatic".
2853 if (!_private->updateFocusedAndActiveStateTimer) {
2854 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
2855 _private->updateFocusedAndActiveStateTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0,
2856 _updateFocusedAndActiveStateTimerCallback, &context);
2857 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateFocusedAndActiveStateTimer, kCFRunLoopDefaultMode);
2860 [[self _pluginController] startAllPlugins];
2862 _private->lastScrollPosition = NSZeroPoint;
2866 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2868 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2871 - (void)viewDidMoveToHostWindow
2873 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2877 - (void)addSubview:(NSView *)view
2879 [super addSubview:view];
2881 if ([WebPluginController isPlugInView:view])
2882 [[self _pluginController] addPlugin:view];
2885 - (void)willRemoveSubview:(NSView *)subview
2887 if ([WebPluginController isPlugInView:subview])
2888 [[self _pluginController] destroyPlugin:subview];
2890 [super willRemoveSubview:subview];
2893 - (void)reapplyStyles
2895 if (!_private->needsToApplyStyles)
2899 double start = CFAbsoluteTimeGetCurrent();
2902 if (Frame* coreFrame = core([self _frame])) {
2903 if (FrameView* coreView = coreFrame->view())
2904 coreView->setMediaType(_private->printing ? "print" : "screen");
2905 if (Document* document = coreFrame->document())
2906 document->setPrinting(_private->printing);
2907 coreFrame->reapplyStyles();
2911 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2912 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2915 _private->needsToApplyStyles = NO;
2918 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2919 // minPageWidth==0 implies a non-printing layout
2920 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
2922 [self reapplyStyles];
2924 if (!_private->needsLayout && ![[self _frame] _needsLayout])
2928 double start = CFAbsoluteTimeGetCurrent();
2931 LOG(View, "%@ doing layout", self);
2933 Frame* coreFrame = core([self _frame]);
2935 _private->needsLayout = NO;
2939 if (FrameView* coreView = coreFrame->view()) {
2940 if (minPageWidth > 0.0)
2941 coreView->forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize);
2943 coreView->forceLayout(!adjustViewSize);
2945 coreView->adjustViewSize();
2948 _private->needsLayout = NO;
2950 if (!_private->printing)
2951 _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
2954 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2955 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
2961 [self layoutToMinimumPageWidth:0.0f maximumPageWidth:0.0f adjustingViewSize:NO];
2964 // Deliver mouseup events to the DOM for button 2.
2965 - (void)rightMouseUp:(NSEvent *)event
2967 [super rightMouseUp:event];
2968 if (Frame* coreframe = core([self _frame]))
2969 coreframe->eventHandler()->mouseUp(event);
2972 - (NSMenu *)menuForEvent:(NSEvent *)event
2974 [_private->compController endRevertingChange:NO moveLeft:NO];
2976 _private->handlingMouseDownEvent = YES;
2977 BOOL handledEvent = NO;
2978 Frame* coreFrame = core([self _frame]);
2981 _private->handlingMouseDownEvent = NO;
2985 Page* page = coreFrame->page();
2989 page->contextMenuController()->clearContextMenu();
2990 // Match behavior of other browsers by sending an onmousedown event for right clicks.
2991 coreFrame->eventHandler()->mouseDown(event);
2992 handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformMouseEvent(event));
2993 _private->handlingMouseDownEvent = NO;
2998 ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
3002 NSArray* menuItems = coreMenu->platformDescription();
3004 if (menuItems && [menuItems count] > 0) {
3005 menu = [[[NSMenu alloc] init] autorelease];
3006 for (unsigned i = 0; i < [menuItems count]; i++)
3007 [menu addItem:[menuItems objectAtIndex:i]];
3013 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
3015 return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
3020 Frame* coreFrame = core([self _frame]);
3023 Document* document = coreFrame->document();
3027 document->setFocusedNode(0);
3032 return [[self _webView] drawsBackground];
3035 - (void)setNeedsDisplay:(BOOL)flag
3037 LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO");
3038 [super setNeedsDisplay:flag];
3041 - (void)setNeedsLayout: (BOOL)flag
3043 LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO");
3044 _private->needsLayout = flag;
3047 - (void)setNeedsToApplyStyles: (BOOL)flag
3049 LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO");
3050 _private->needsToApplyStyles = flag;
3053 - (void)drawSingleRect:(NSRect)rect
3055 [NSGraphicsContext saveGraphicsState];
3058 ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
3060 [(WebClipView *)[self superview] setAdditionalClip:rect];
3063 if ([self _transparentBackground]) {
3064 [[NSColor clearColor] set];
3068 [[self _frame] _drawRect:rect contentsOnly:YES];
3070 WebView *webView = [self _webView];
3072 // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
3073 // called after the WebView has closed. If the client did not properly close the WebView and set the
3074 // UIDelegate to nil, then the UIDelegate will be stale and this code will crash.
3075 static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
3076 if (version3OrLaterClient)
3077 [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
3079 if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight])
3080 [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]];
3082 [(WebClipView *)[self superview] resetAdditionalClip];
3084 [NSGraphicsContext restoreGraphicsState];
3085 } @catch (NSException *localException) {
3086 [(WebClipView *)[self superview] resetAdditionalClip];
3087 [NSGraphicsContext restoreGraphicsState];
3088 LOG_ERROR("Exception caught while drawing: %@", localException);
3089 [localException raise];
3093 - (void)drawRect:(NSRect)rect
3095 ASSERT_MAIN_THREAD();
3096 LOG(View, "%@ drawing", self);
3098 const NSRect *rects;
3100 [self getRectsBeingDrawn:&rects count:&count];
3102 BOOL subviewsWereSetAside = _private->subviewsSetAside;
3103 if (subviewsWereSetAside)
3104 [self _restoreSubviews];
3107 double start = CFAbsoluteTimeGetCurrent();
3110 if ([[self _webView] _mustDrawUnionedRect:rect singleRects:rects count:count])
3111 [self drawSingleRect:rect];
3113 for (int i = 0; i < count; ++i)
3114 [self drawSingleRect:rects[i]];
3117 double thisTime = CFAbsoluteTimeGetCurrent() - start;
3118 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
3121 if (subviewsWereSetAside)
3122 [self _setAsideSubviews];
3124 #if USE(ACCELERATED_COMPOSITING)
3125 if ([[self _webView] _needsOneShotDrawingSynchronization]) {
3126 // Disable screen updates so that drawing into the NSView and
3127 // CALayer updates appear on the screen at the same time.
3128 [[self window] disableScreenUpdatesUntilFlush];
3129 [CATransaction flush];
3130 [[self _webView] _setNeedsOneShotDrawingSynchronization:NO];
3135 // Turn off the additional clip while computing our visibleRect.
3136 - (NSRect)visibleRect
3138 if (!([[self superview] isKindOfClass:[WebClipView class]]))
3139 return [super visibleRect];
3141 WebClipView *clipView = (WebClipView *)[self superview];
3143 BOOL hasAdditionalClip = [clipView hasAdditionalClip];
3144 if (!hasAdditionalClip) {
3145 return [super visibleRect];
3148 NSRect additionalClip = [clipView additionalClip];
3149 [clipView resetAdditionalClip];
3150 NSRect visibleRect = [super visibleRect];
3151 [clipView setAdditionalClip:additionalClip];
3160 - (void)windowDidBecomeKey:(NSNotification *)notification
3162 NSWindow *keyWindow = [notification object];
3164 if (keyWindow == [self window])
3165 [self addMouseMovedObserver];
3167 if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
3168 [self _updateFocusedAndActiveState];
3171 - (void)windowDidResignKey:(NSNotification *)notification
3173 NSWindow *formerKeyWindow = [notification object];
3175 if (formerKeyWindow == [self window])
3176 [self removeMouseMovedObserver];
3178 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
3179 [self _updateFocusedAndActiveState];
3180 [_private->compController endRevertingChange:NO moveLeft:NO];
3184 - (void)windowWillClose:(NSNotification *)notification
3186 [_private->compController endRevertingChange:NO moveLeft:NO];
3187 [[self _pluginController] destroyAllPlugins];
3190 - (void)windowWillOrderOnScreen:(NSNotification *)notification
3192 if (![[self _webView] shouldUpdateWhileOffscreen])
3193 [self setNeedsDisplay:YES];
3196 - (void)scrollWheel:(NSEvent *)event
3199 Frame* frame = core([self _frame]);
3200 if (!frame || !frame->eventHandler()->wheelEvent(event))
3201 [super scrollWheel:event];
3205 - (BOOL)_isSelectionEvent:(NSEvent *)event
3207 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3208 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
3211 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3213 NSView *hitView = [self _hitViewForEvent:event];
3214 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3216 #if ENABLE(DASHBOARD_SUPPORT)
3217 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
3222 bool result = false;
3223 if (Frame* coreFrame = core([hitHTMLView _frame])) {
3224 coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
3225 [hitHTMLView _setMouseDownEvent:event];
3226 if ([hitHTMLView _isSelectionEvent:event])
3227 result = coreFrame->eventHandler()->eventMayStartDrag(event);
3228 [hitHTMLView _setMouseDownEvent:nil];
3232 return [hitView acceptsFirstMouse:event];
3235 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3237 NSView *hitView = [self _hitViewForEvent:event];
3238 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3240 bool result = false;
3241 if ([hitHTMLView _isSelectionEvent:event])
3242 if (Frame* coreFrame = core([hitHTMLView _frame])) {
3243 [hitHTMLView _setMouseDownEvent:event];
3244 result = coreFrame->eventHandler()->eventMayStartDrag(event);
3245 [hitHTMLView _setMouseDownEvent:nil];
3249 return [hitView shouldDelayWindowOrderingForEvent:event];
3252 - (void)mouseDown:(NSEvent *)event
3254 RetainPtr<WebHTMLView> protector = self;
3255 if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
3258 _private->handlingMouseDownEvent = YES;
3260 // Record the mouse down position so we can determine drag hysteresis.
3261 [self _setMouseDownEvent:event];
3263 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3264 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3267 [_private->compController endRevertingChange:NO moveLeft:NO];
3269 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
3270 // We don't want to pass them along to KHTML a second time.
3271 if (!([event modifierFlags] & NSControlKeyMask)) {
3272 _private->ignoringMouseDraggedEvents = NO;
3274 // Don't do any mouseover while the mouse is down.
3275 [self _cancelUpdateMouseoverTimer];
3277 // Let WebCore get a chance to deal with the event. This will call back to us
3278 // to start the autoscroll timer if appropriate.
3279 if (Frame* coreframe = core([self _frame]))
3280 coreframe->eventHandler()->mouseDown(event);
3284 _private->handlingMouseDownEvent = NO;
3287 - (void)dragImage:(NSImage *)dragImage
3289 offset:(NSSize)offset
3290 event:(NSEvent *)event
3291 pasteboard:(NSPasteboard *)pasteboard
3293 slideBack:(BOOL)slideBack
3295 ASSERT(self == [self _topHTMLView]);
3296 [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
3299 - (void)mouseDragged:(NSEvent *)event
3301 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3302 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3307 if (!_private->ignoringMouseDraggedEvents)
3308 if (Frame* coreframe = core([self _frame]))
3309 coreframe->eventHandler()->mouseDragged(event);
3314 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
3316 ASSERT(![self _webView] || [self _isTopHTMLView]);
3318 Page *page = core([self _webView]);
3321 return NSDragOperationNone;
3323 if (page->dragController()->dragOperation() == DragOperationNone)
3324 return NSDragOperationGeneric | NSDragOperationCopy;
3326 return (NSDragOperation)page->dragController()->dragOperation();
3329 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
3331 ASSERT(![self _webView] || [self _isTopHTMLView]);
3333 NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
3334 NSPoint windowMouseLoc = windowImageLoc;
3336 if (Page* page = core([self _webView])) {
3337 DragController* dragController = page->dragController();
3338 NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3341 [[self _frame] _dragSourceMovedTo:windowMouseLoc];
3344 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
3346 ASSERT(![self _webView] || [self _isTopHTMLView]);
3348 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
3349 NSPoint windowMouseLoc = windowImageLoc;
3351 if (Page* page = core([self _webView])) {
3352 DragController* dragController = page->dragController();
3353 windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3354 dragController->dragEnded();
3357 [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation];
3359 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3360 _private->ignoringMouseDraggedEvents = YES;
3362 // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
3363 // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
3364 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
3365 location:windowMouseLoc
3366 modifierFlags:[[NSApp currentEvent] modifierFlags]
3367 timestamp:[NSDate timeIntervalSinceReferenceDate]
3368 windowNumber:[[self window] windowNumber]
3369 context:[[NSApp currentEvent] context]
3370 eventNumber:0 clickCount:0 pressure:0];
3371 [self mouseUp:fakeEvent]; // This will also update the mouseover state.
3374 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3376 NSFileWrapper *wrapper = nil;
3377 NSURL *draggingImageURL = nil;
3379 if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) {
3381 SharedBuffer *buffer = static_cast<CachedResource*>(tiffResource)->data();
3383 goto noPromisedData;
3385 NSData *data = buffer->createNSData();
3386 NSURLResponse *response = tiffResource->response().nsURLResponse();
3387 draggingImageURL = [response URL];
3388 wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
3389 NSString* filename = [response suggestedFilename];
3390 NSString* trueExtension(tiffResource->image()->filenameExtension());
3391 if (![filename matchesExtensionEquivalent:trueExtension])
3392 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension];
3393 [wrapper setPreferredFilename:filename];
3399 ASSERT(![self _webView] || [self _isTopHTMLView]);
3400 Page* page = core([self _webView]);
3402 //If a load occurs midway through a drag, the view may be detached, which gives
3403 //us no ability to get to the original Page, so we cannot access any drag state
3404 //FIXME: is there a way to recover?
3408 const KURL& imageURL = page->dragController()->draggingImageURL();
3409 ASSERT(!imageURL.isEmpty());
3410 draggingImageURL = imageURL;
3412 wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL];
3415 if (wrapper == nil) {
3416 LOG_ERROR("Failed to create image file.");
3420 // FIXME: Report an error if we fail to create a file.
3421 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3422 path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
3423 if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
3424 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
3426 if (draggingImageURL)
3427 [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path];
3429 return [NSArray arrayWithObject:[path lastPathComponent]];
3432 - (void)mouseUp:(NSEvent *)event
3434 [self _setMouseDownEvent:nil];
3436 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3437 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3442 [self _stopAutoscrollTimer];
3443 if (Frame* coreframe = core([self _frame]))
3444 coreframe->eventHandler()->mouseUp(event);
3445 [self _updateMouseoverWithFakeEvent];
3450 - (void)mouseMovedNotification:(NSNotification *)notification
3452 [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
3455 // returning YES from this method is the way we tell AppKit that it is ok for this view
3456 // to be in the key loop even when "tab to all controls" is not on.
3457 - (BOOL)needsPanelToBecomeKey
3462 - (BOOL)becomeFirstResponder
3464 NSSelectionDirection direction = NSDirectSelection;
3465 if (![[self _webView] _isPerformingProgrammaticFocus])
3466 direction = [[self window] keyViewSelectionDirection];
3468 [self _updateFocusedAndActiveState];
3469 [self _updateFontPanel];
3471 Frame* frame = core([self _frame]);
3475 frame->editor()->setStartNewKillRingSequence(true);
3477 if (direction == NSDirectSelection)
3480 Page* page = frame->page();
3484 page->focusController()->setFocusedFrame(frame);
3485 if (Document* document = frame->document())
3486 document->setFocusedNode(0);
3487 page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward,
3488 frame->eventHandler()->currentKeyboardEvent().get());
3492 - (BOOL)resignFirstResponder
3494 BOOL resign = [super resignFirstResponder];
3496 _private->resigningFirstResponder = YES;
3497 [_private->compController endRevertingChange:NO moveLeft:NO];
3498 if (![self maintainsInactiveSelection]) {
3500 if (![[self _webView] _isPerformingProgrammaticFocus])
3503 [self _updateFocusedAndActiveState];
3504 _private->resigningFirstResponder = NO;
3509 - (void)setDataSource:(WebDataSource *)dataSource
3512 if (_private->dataSource != dataSource) {
3513 ASSERT(!_private->closed);
3514 BOOL hadDataSource = _private->dataSource != nil;
3516 [dataSource retain];
3517 [_private->dataSource release];
3518 _private->dataSource = dataSource;
3519 [_private->pluginController setDataSource:dataSource];
3522 [self addMouseMovedObserver];
3526 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3530 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3531 // key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3532 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3533 - (void)updateCell:(NSCell*)cell
3537 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3538 // pageWidth != 0 implies we will relayout to a new width
3539 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
3541 WebFrame *frame = [self _frame];
3542 NSArray *subframes = [frame childFrames];
3543 unsigned n = [subframes count];
3545 for (i = 0; i != n; ++i) {
3546 WebFrame *subframe = [subframes objectAtIndex:i];
3547 WebFrameView *frameView = [subframe frameView];
3548 if ([[subframe _dataSource] _isDocumentHTML]) {
3549 [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:adjustViewSize];
3553 if (printing != _private->printing) {
3554 [_private->pageRects release];
3555 _private->pageRects = nil;
3556 _private->printing = printing;
3558 _private->avoidingPrintOrphan = NO;
3559 [self setNeedsToApplyStyles:YES];
3560 [self setNeedsLayout:YES];
3561 [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
3563 // Can't do this when starting printing or nested printing won't work, see 3491427.
3564 [self setNeedsDisplay:NO];
3569 - (BOOL)canPrintHeadersAndFooters
3574 // This is needed for the case where the webview is embedded in the view that's being printed.
3575 // It shouldn't be called when the webview is being printed directly.
3576 - (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit
3578 // This helps when we print as part of a larger print process.
3579 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3580 BOOL wasInPrintingMode = _private->printing;
3581 if (!wasInPrintingMode)
3582 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3584 float newBottomFloat = *newBottom;
3585 if (FrameView* view = core([self _frame])->view())
3586 view->adjustPageHeight(&newBottomFloat, oldTop, oldBottom, bottomLimit);
3589 // If the new bottom is equal to the old bottom (when both are treated as floats), we just copy
3590 // oldBottom over to newBottom. This prevents rounding errors that can occur when converting newBottomFloat to a double.
3591 if (fabs((float)oldBottom - newBottomFloat) <= std::numeric_limits<float>::epsilon())
3592 *newBottom = oldBottom;
3595 *newBottom = newBottomFloat;
3597 if (!wasInPrintingMode) {
3598 NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation];
3599 if (currenPrintOperation)
3600 // delay _setPrinting:NO until back to main loop as this method may get called repeatedly
3601 [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0];
3603 // not sure if this is actually ever invoked, it probably shouldn't be
3604 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3608 - (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
3610 NSPrintInfo *printInfo = [printOperation printInfo];
3611 return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
3614 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3616 float viewWidth = NSWidth([self bounds]);
3617 if (viewWidth < 1) {
3618 LOG_ERROR("%@ has no width when printing", self);
3622 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3623 float maxShrinkToFitScaleFactor = 1.0f / PrintingMaximumShrinkFactor;
3624 float shrinkToFitScaleFactor = [self _availablePaperWidthForPrintOperation:printOperation]/viewWidth;
3625 float shrinkToAvoidOrphan = _private->avoidingPrintOrphan ? (1.0f / PrintingOrphanShrinkAdjustment) : 1.0f;
3626 return userScaleFactor * MAX(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor) * shrinkToAvoidOrphan;
3629 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3630 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3631 // if AppKit makes it SPI/API.
3632 - (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3634 return [self _scaleFactorForPrintOperation:printOperation];
3637 // This is used for Carbon printing. At some point we might want to make this public API.
3638 - (void)setPageWidthForPrinting:(float)pageWidth
3640 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3641 [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
3644 - (void)_endPrintMode
3646 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
3647 [[self window] setAutodisplay:YES];
3650 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3652 ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3653 NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3654 if (initiatingOperation == currentOperation) {
3655 // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3656 // being extra paranoid here since the printing code is so fragile. Delay the cleanup
3658 ASSERT_NOT_REACHED();
3659 [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3660 } else if ([currentOperation view] == self) {
3661 // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3662 // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3663 // fragile. Do nothing, because we don't want to break the print job currently in progress, and
3664 // the print job currently in progress is responsible for its own cleanup.
3665 ASSERT_NOT_REACHED();
3667 // The print job that kicked off this delayed call has finished, and this view is not being
3668 // printed again. We expect that no other print job has started. Since this delayed call wasn't
3669 // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3670 // the print mode here.
3671 ASSERT(currentOperation == nil);
3672 [self _endPrintMode];
3676 // Return the number of pages available for printing
3677 - (BOOL)knowsPageRange:(NSRangePointer)range
3679 // Must do this explicit display here, because otherwise the view might redisplay while the print
3680 // sheet was up, using printer fonts (and looking different).
3681 [self displayIfNeeded];
3682 [[self window] setAutodisplay:NO];
3684 // If we are a frameset just print with the layout we have onscreen, otherwise relayout
3685 // according to the paper size
3686 float minLayoutWidth = 0.0f;
3687 float maxLayoutWidth = 0.0f;
3688 Frame* frame = core([self _frame]);
3691 if (!frame->document() || !frame->document()->isFrameSet()) {
3692 float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
3693 minLayoutWidth = paperWidth * PrintingMinimumShrinkFactor;
3694 maxLayoutWidth = paperWidth * PrintingMaximumShrinkFactor;
3696 [self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; // will relayout
3697 NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3698 // Certain types of errors, including invalid page ranges, can cause beginDocument and
3699 // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3700 // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3701 // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3702 // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3703 // which is after beginDocument and endDocument would be called.
3704 [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3705 [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3707 // There is a theoretical chance that someone could do some drawing between here and endDocument,
3708 // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3709 // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3711 range->location = 1;
3712 float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3713 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3714 [_private->pageRects release];
3715 float fullPageHeight = floorf([self _calculatePrintHeight]/totalScaleFactor);
3716 NSArray *newPageRects = [[self _frame] _computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3717 printHeight:fullPageHeight];
3719 // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3720 // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3721 // a blank page (with correct-looking header and footer if that option is on), which matches
3722 // the behavior of IE and Camino at least.
3723 if ([newPageRects count] == 0)
3724 newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3725 else if ([newPageRects count] > 1) {
3726 // If the last page is a short orphan, try adjusting the print height slightly to see if this will squeeze the
3727 // content onto one fewer page. If it does, use the adjusted scale. If not, use the original scale.
3728 float lastPageHeight = NSHeight([[newPageRects lastObject] rectValue]);
3729 if (lastPageHeight/fullPageHeight < LastPrintedPageOrphanRatio) {
3730 NSArray *adjustedPageRects = [[self _frame] _computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3731 printHeight:fullPageHeight*PrintingOrphanShrinkAdjustment];
3732 // Use the adjusted rects only if the page count went down
3733 if ([adjustedPageRects count] < [newPageRects count]) {
3734 newPageRects = adjustedPageRects;
3735 _private->avoidingPrintOrphan = YES;
3740 _private->pageRects = [newPageRects retain];
3742 range->length = [_private->pageRects count];
3747 // Return the drawing rectangle for a particular page number
3748 - (NSRect)rectForPage:(NSInteger)page
3750 return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3753 - (void)drawPageBorderWithSize:(NSSize)borderSize
3755 ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));
3756 [[self _webView] _drawHeaderAndFooter];
3759 - (void)beginDocument
3762 // From now on we'll get a chance to call _endPrintMode in either beginDocument or
3763 // endDocument, so we can cancel the "just in case" pending call.
3764 [NSObject cancelPreviousPerformRequestsWithTarget:self
3765 selector:@selector(_delayedEndPrintMode:)
3766 object:[NSPrintOperation currentOperation]];
3767 [super beginDocument];
3768 } @catch (NSException *localException) {
3769 // Exception during [super beginDocument] means that endDocument will not get called,
3770 // so we need to clean up our "print mode" here.
3771 [self _endPrintMode];
3777 [super endDocument];
3778 // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO
3779 [self _endPrintMode];
3782 - (void)keyDown:(NSEvent *)event
3784 RetainPtr<WebHTMLView> selfProtector = self;
3785 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
3787 BOOL callSuper = NO;
3789 [_private->keyDownEvent release];
3790 _private->keyDownEvent = [event retain];
3792 BOOL completionPopupWasOpen = _private->compController && [_private->compController popupWindowIsOpen];
3793 Frame* coreFrame = core([self _frame]);
3794 if (!eventWasSentToWebCore && coreFrame && coreFrame->eventHandler()->keyEvent(event)) {
3795 // WebCore processed a key event, bail on any preexisting complete: UI
3796 if (completionPopupWasOpen)
3797 [_private->compController endRevertingChange:YES moveLeft:NO];
3798 } else if (!_private->compController || ![_private->compController filterKeyDown:event]) {
3799 // Not consumed by complete: popup window
3800 [_private->compController endRevertingChange:YES moveLeft:NO];
3804 [super keyDown:event];
3806 [NSCursor setHiddenUntilMouseMoves:YES];
3809 - (void)keyUp:(NSEvent *)event
3811 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
3813 RetainPtr<WebHTMLView> selfProtector = self;
3814 Frame* coreFrame = core([self _frame]);
3815 if (coreFrame && !eventWasSentToWebCore)
3816 coreFrame->eventHandler()->keyEvent(event);
3818 [super keyUp:event];
3821 - (void)flagsChanged:(NSEvent *)event
3823 Frame* coreFrame = core([self _frame]);
3825 coreFrame->eventHandler()->capsLockStateMayHaveChanged();
3827 RetainPtr<WebHTMLView> selfProtector = self;
3829 unsigned short keyCode = [event keyCode];
3831 //Don't make an event from the num lock and function keys
3832 if (coreFrame && keyCode != 0 && keyCode != 10 && keyCode != 63) {
3833 coreFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(event));
3837 [super flagsChanged:event];
3840 - (id)accessibilityAttributeValue:(NSString*)attributeName
3842 if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
3843 id accTree = [[self _frame] _accessibilityTree];
3845 return [NSArray arrayWithObject:accTree];
3848 return [super accessibilityAttributeValue:attributeName];
3851 - (id)accessibilityFocusedUIElement
3853 id accTree = [[self _frame] _accessibilityTree];
3855 return [accTree accessibilityFocusedUIElement];
3859 - (id)accessibilityHitTest:(NSPoint)point
3861 id accTree = [[self _frame] _accessibilityTree];
3863 NSPoint windowCoord = [[self window] convertScreenToBase:point];
3864 return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
3869 - (id)_accessibilityParentForSubview:(NSView *)subview
3871 id accTree = [[self _frame] _accessibilityTree];
3874 id parent = [accTree _accessibilityParentForSubview:subview];
3880 - (void)centerSelectionInVisibleArea:(id)sender
3884 if (Frame* coreFrame = core([self _frame]))
3885 coreFrame->revealSelection(ScrollAlignment::alignCenterAlways);
3888 - (NSData *)_selectionStartFontAttributesAsRTF
3890 Frame* coreFrame = core([self _frame]);
3891 NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x"
3892 attributes:coreFrame ? coreFrame->fontAttributesForSelectionStart() : nil];
3893 NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
3898 - (NSDictionary *)_fontAttributesFromFontPasteboard
3900 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
3901 if (fontPasteboard == nil)
3903 NSData *data = [fontPasteboard dataForType:NSFontPboardType];
3904 if (data == nil || [data length] == 0)
3906 // NSTextView does something more efficient by parsing the attributes only, but that's not available in API.
3907 NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease];
3908 if (string == nil || [string length] == 0)
3910 return [string fontAttributesInRange:NSMakeRange(0, 1)];
3913 - (DOMCSSStyleDeclaration *)_emptyStyle
3915 return [[[self _frame] DOMDocument] createCSSStyleDeclaration];
3918 - (NSString *)_colorAsString:(NSColor *)color
3920 NSColor *rgbColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
3921 // FIXME: If color is non-nil and rgbColor is nil, that means we got some kind
3922 // of fancy color that can't be converted to RGB. Changing that to "transparent"
3923 // might not be great, but it's probably OK.
3924 if (rgbColor == nil)
3925 return @"transparent";
3926 float r = [rgbColor redComponent];
3927 float g = [rgbColor greenComponent];
3928 float b = [rgbColor blueComponent];
3929 float a = [rgbColor alphaComponent];
3931 return @"transparent";
3932 if (r == 0 && g == 0 && b == 0 && a == 1)
3934 if (r == 1 && g == 1 && b == 1 && a == 1)
3936 // FIXME: Lots more named colors. Maybe we could use the table in WebCore?
3938 return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255];
3939 return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a];
3942 - (NSString *)_shadowAsString:(NSShadow *)shadow
3946 NSSize offset = [shadow shadowOffset];
3947 float blurRadius = [shadow shadowBlurRadius];
3948 if (offset.width == 0 && offset.height == 0 && blurRadius == 0)
3950 NSColor *color = [shadow shadowColor];
3953 // FIXME: Handle non-integral values here?
3954 if (blurRadius == 0)
3955 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height];
3956 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius];
3959 - (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary
3961 DOMCSSStyleDeclaration *style = [self _emptyStyle];
3963 NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName];
3964 [style setBackgroundColor:[self _colorAsString:color]];
3966 NSFont *font = [dictionary objectForKey:NSFontAttributeName];
3968 [style setFontFamily:@"Helvetica"];
3969 [style setFontSize:@"12px"];
3970 [style setFontWeight:@"normal"];
3971 [style setFontStyle:@"normal"];
3973 NSFontManager *fm = [NSFontManager sharedFontManager];
3974 // FIXME: Need more sophisticated escaping code if we want to handle family names
3975 // with characters like single quote or backslash in their names.
3976 [style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]];
3977 [style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]];
3978 // FIXME: Map to the entire range of CSS weight values.
3979 if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT)
3980 [style setFontWeight:@"bold"];
3982 [style setFontWeight:@"normal"];
3983 if ([fm traitsOfFont:font] & NSItalicFontMask)
3984 [style setFontStyle:@"italic"];
3986 [style setFontStyle:@"normal"];
3989 color = [dictionary objectForKey:NSForegroundColorAttributeName];
3990 [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"];
3992 NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName];
3993 [style setTextShadow:[self _shadowAsString:shadow]];
3995 int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue];
3997 int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue];
3998 if (superscriptInt > 0)
3999 [style setVerticalAlign:@"super"];
4000 else if (superscriptInt < 0)
4001 [style setVerticalAlign:@"sub"];
4003 [style setVerticalAlign:@"baseline"];
4004 int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue];
4005 // FIXME: Underline wins here if we have both (see bug 3790443).
4006 if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone)
4007 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
4008 else if (underlineInt == NSUnderlineStyleNone)
4009 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
4011 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
4016 - (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
4018 if (Frame* coreFrame = core([self _frame]))
4019 coreFrame->editor()->applyStyleToSelection(core(style), undoAction);
4022 - (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
4024 if (Frame* coreFrame = core([self _frame]))
4025 coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction);
4028 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
4030 ASSERT([self _webView]);
4031 if (![[[self _webView] preferences] respectStandardStyleKeyEquivalents])
4034 if (![self _canEdit])
4037 if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
4040 NSString *string = [event characters];
4041 if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
4042 [self executeCoreCommandByName:"ToggleBold"];
4045 if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
4046 [self executeCoreCommandByName:"ToggleItalic"];
4053 - (BOOL)performKeyEquivalent:(NSEvent *)event
4055 if ([self _handleStyleKeyEquivalent:event])
4058 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
4061 [_private->keyDownEvent release];
4062 _private->keyDownEvent = [event retain];
4066 // Pass command-key combos through WebCore if there is a key binding available for
4067 // this event. This lets web pages have a crack at intercepting command-modified keypresses.
4068 // But don't do it if we have already handled the event.
4069 // Pressing Esc results in a fake event being sent - don't pass it to WebCore.
4070 if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder])
4071 if (Frame* frame = core([self _frame]))
4072 ret = frame->eventHandler()->keyEvent(event);
4075 ret = [super performKeyEquivalent:event];
4082 - (void)copyFont:(id)sender
4086 // Put RTF with font attributes on the pasteboard.
4087 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
4088 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
4089 [fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil];
4090 [fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType];
4093 - (void)pasteFont:(id)sender
4097 // Read RTF with font attributes from the pasteboard.
4098 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
4099 [self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:EditActionPasteFont];
4102 - (void)pasteAsRichText:(id)sender
4106 // Since rich text always beats plain text when both are on the pasteboard, it's not
4107 // clear how this is different from plain old paste.
4108 [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO];
4111 - (NSFont *)_originalFontA
4113 return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f];
4116 - (NSFont *)_originalFontB
4118 return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:NSFontItalicTrait weight:STANDARD_BOLD_WEIGHT size:12.0f];
4121 - (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b
4123 // Since there's no way to directly ask NSFontManager what style change it's going to do
4124 // we instead pass two "specimen" fonts to it and let it change them. We then deduce what
4125 // style change it was doing by looking at what happened to each of the two fonts.
4126 // So if it was making the text bold, both fonts will be bold after the fact.
4128 if (a == nil || b == nil)
4131 NSFontManager *fm = [NSFontManager sharedFontManager];
4133 NSFont *oa = [self _originalFontA];