2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #import "WebHTMLView.h"
32 #import "DOMNodeInternal.h"
33 #import "DOMRangeInternal.h"
34 #import "WebArchive.h"
35 #import "WebNetscapePluginView.h"
36 #import "WebClipView.h"
37 #import "WebDOMOperationsPrivate.h"
38 #import "WebDataSourceInternal.h"
39 #import "WebDefaultUIDelegate.h"
40 #import "WebDocumentInternal.h"
41 #import "WebDynamicScrollBarsView.h"
42 #import "WebEditingDelegate.h"
43 #import "WebElementDictionary.h"
44 #import "WebFrameInternal.h"
45 #import "WebFramePrivate.h"
46 #import "WebFrameViewInternal.h"
47 #import "WebHTMLRepresentationPrivate.h"
48 #import "WebHTMLViewInternal.h"
49 #import "WebKitLogging.h"
50 #import "WebKitNSStringExtras.h"
51 #import "WebKitVersionChecks.h"
52 #import "WebLocalizableStrings.h"
53 #import "WebNodeHighlight.h"
54 #import "WebNSAttributedStringExtras.h"
55 #import "WebNSEventExtras.h"
56 #import "WebNSFileManagerExtras.h"
57 #import "WebNSImageExtras.h"
58 #import "WebNSObjectExtras.h"
59 #import "WebNSPasteboardExtras.h"
60 #import "WebNSPrintOperationExtras.h"
61 #import "WebNSURLExtras.h"
62 #import "WebNSViewExtras.h"
63 #import "WebPluginController.h"
64 #import "WebPreferences.h"
65 #import "WebPreferencesPrivate.h"
66 #import "WebResourcePrivate.h"
67 #import "WebStringTruncator.h"
68 #import "WebTypesInternal.h"
69 #import "WebUIDelegatePrivate.h"
70 #import "WebViewInternal.h"
71 #import <AppKit/NSAccessibility.h>
72 #import <ApplicationServices/ApplicationServices.h>
74 #import <WebCore/CachedImage.h>
75 #import <WebCore/CachedResourceClient.h>
76 #import <WebCore/ColorMac.h>
77 #import <WebCore/ContextMenu.h>
78 #import <WebCore/ContextMenuController.h>
79 #import <WebCore/Document.h>
80 #import <WebCore/DocumentFragment.h>
81 #import <WebCore/Editor.h>
82 #import <WebCore/EditorDeleteAction.h>
83 #import <WebCore/Element.h>
84 #import <WebCore/EventHandler.h>
85 #import <WebCore/ExceptionHandlers.h>
86 #import <WebCore/DragController.h>
87 #import <WebCore/FloatRect.h>
88 #import <WebCore/FocusController.h>
89 #import <WebCore/Frame.h>
90 #import <WebCore/FrameLoader.h>
91 #import <WebCore/FrameView.h>
92 #import <WebCore/HitTestResult.h>
93 #import <WebCore/HTMLNames.h>
94 #import <WebCore/Image.h>
95 #import <WebCore/KeyboardEvent.h>
96 #import <WebCore/LegacyWebArchive.h>
97 #import <WebCore/MIMETypeRegistry.h>
98 #import <WebCore/Page.h>
99 #import <WebCore/PlatformKeyboardEvent.h>
100 #import <WebCore/PlatformMouseEvent.h>
101 #import <WebCore/Range.h>
102 #import <WebCore/SelectionController.h>
103 #import <WebCore/SharedBuffer.h>
104 #import <WebCore/SimpleFontData.h>
105 #import <WebCore/Text.h>
106 #import <WebCore/WebCoreObjCExtras.h>
107 #import <WebCore/WebCoreTextRenderer.h>
108 #import <WebCore/markup.h>
109 #import <WebKit/DOM.h>
110 #import <WebKit/DOMExtensions.h>
111 #import <WebKit/DOMPrivate.h>
112 #import <WebKitSystemInterface.h>
114 #import <runtime/InitializeThreading.h>
116 #if USE(ACCELERATED_COMPOSITING)
117 #import <QuartzCore/QuartzCore.h>
120 using namespace WebCore;
121 using namespace HTMLNames;
124 @interface NSWindow (BorderViewAccess)
125 - (NSView*)_web_borderView;
128 @implementation NSWindow (BorderViewAccess)
129 - (NSView*)_web_borderView
135 static IMP oldSetCursorIMP = NULL;
137 #ifdef BUILDING_ON_TIGER
138 static IMP oldResetCursorRectsIMP = NULL;
139 static BOOL canSetCursor = YES;
141 static void resetCursorRects(NSWindow* self, SEL cmd)
143 NSPoint point = [self mouseLocationOutsideOfEventStream];
144 NSView* view = [[self _web_borderView] hitTest:point];
145 if ([view isKindOfClass:[WebHTMLView class]]) {
146 WebHTMLView *htmlView = (WebHTMLView*)view;
147 NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
148 NSDictionary *dict = [htmlView elementAtPoint:localPoint allowShadowContent:NO];
149 DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
150 if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
151 ![element isKindOfClass:[DOMHTMLEmbedElement class]])
154 oldResetCursorRectsIMP(self, cmd);
158 static void setCursor(NSCursor* self, SEL cmd)
161 oldSetCursorIMP(self, cmd);
164 static void setCursor(NSWindow* self, SEL cmd, NSPoint point)
166 NSView* view = [[self _web_borderView] hitTest:point];
167 if ([view isKindOfClass:[WebHTMLView class]]) {
168 WebHTMLView *htmlView = (WebHTMLView*)view;
169 NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
170 NSDictionary *dict = [htmlView elementAtPoint:localPoint allowShadowContent:NO];
171 DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
172 if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
173 ![element isKindOfClass:[DOMHTMLEmbedElement class]])
176 oldSetCursorIMP(self, cmd, point);
180 #if USE(ACCELERATED_COMPOSITING)
181 @interface WebLayerHostingView : NSView
184 @implementation WebLayerHostingView
185 // Empty NSViews intercept rightMouseDown: to do context menu handling, but we need the WebLayerHostingView to
186 // let right mouse clicks through.
187 - (void)rightMouseDown:(NSEvent *)theEvent
189 [[self nextResponder] performSelector:_cmd withObject:theEvent];
192 #endif // USE(ACCELERATED_COMPOSITING)
196 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
198 extern NSString *NSMarkedClauseSegmentAttributeName;
199 extern NSString *NSTextInputReplacementRangeAttributeName;
202 @interface NSView (WebNSViewDetails)
203 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
204 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
205 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView;
206 - (NSRect)_dirtyRect;
207 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
208 - (void)_propagateDirtyRectsToOpaqueAncestors;
209 - (void)_windowChangedKeyState;
212 @interface NSApplication (WebNSApplicationDetails)
213 - (void)speakString:(NSString *)string;
216 @interface NSWindow (WebNSWindowDetails)
217 - (id)_newFirstResponderAfterResigning;
218 - (void)_setForceActiveControls:(BOOL)flag;
221 @interface NSAttributedString (WebNSAttributedStringDetails)
222 - (id)_initWithDOMRange:(DOMRange *)range;
223 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
226 @interface NSSpellChecker (WebNSSpellCheckerDetails)
227 - (void)learnWord:(NSString *)word;
230 // By imaging to a width a little wider than the available pixels,
231 // thin pages will be scaled down a little, matching the way they
232 // print in IE and Camino. This lets them use fewer sheets than they
233 // would otherwise, which is presumably why other browsers do this.
234 // Wide pages will be scaled down more than this.
235 #define PrintingMinimumShrinkFactor 1.25f
237 // This number determines how small we are willing to reduce the page content
238 // in order to accommodate the widest line. If the page would have to be
239 // reduced smaller to make the widest line fit, we just clip instead (this
240 // behavior matches MacIE and Mozilla, at least)
241 #define PrintingMaximumShrinkFactor 2.0f
243 // This number determines how short the last printed page of a multi-page print session
244 // can be before we try to shrink the scale in order to reduce the number of pages, and
245 // thus eliminate the orphan.
246 #define LastPrintedPageOrphanRatio 0.1f
248 // This number determines the amount the scale factor is adjusted to try to eliminate orphans.
249 // It has no direct mathematical relationship to LastPrintedPageOrphanRatio, due to variable
250 // numbers of pages, logic to avoid breaking elements, and CSS-supplied hard page breaks.
251 #define PrintingOrphanShrinkAdjustment 1.1f
253 #define AUTOSCROLL_INTERVAL 0.1f
255 #define DRAG_LABEL_BORDER_X 4.0f
256 //Keep border_y in synch with DragController::LinkDragBorderInset
257 #define DRAG_LABEL_BORDER_Y 2.0f
258 #define DRAG_LABEL_RADIUS 5.0f
259 #define DRAG_LABEL_BORDER_Y_OFFSET 2.0f
261 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120.0f
262 #define MAX_DRAG_LABEL_WIDTH 320.0f
264 #define DRAG_LINK_LABEL_FONT_SIZE 11.0f
265 #define DRAG_LINK_URL_FONT_SIZE 10.0f
267 // Any non-zero value will do, but using something recognizable might help us debug some day.
268 #define TRACKING_RECT_TAG 0xBADFACE
270 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
271 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
273 #define STANDARD_WEIGHT 5
274 #define MIN_BOLD_WEIGHT 7
275 #define STANDARD_BOLD_WEIGHT 9
278 #define WebDataProtocolScheme @"webkit-fake-url"
280 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
281 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
282 @interface WebCoreScrollView : NSScrollView
285 @implementation WebCoreScrollView
288 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
289 static BOOL forceNSViewHitTest;
291 // if YES, do the "top WebHTMLView" hit test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721])
292 static BOOL forceWebHTMLViewHitTest;
294 static WebHTMLView *lastHitView;
296 // We need this to be able to safely reference the CachedImage for the promised drag data
297 static CachedResourceClient* promisedDataClient()
299 static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
300 return staticCachedResourceClient;
303 @interface WebHTMLView (WebHTMLViewFileInternal)
304 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
305 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
306 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
307 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
308 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
309 - (void)_removeMouseMovedObserverUnconditionally;
310 - (void)_removeSuperviewObservers;
311 - (void)_removeWindowObservers;
312 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
313 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
314 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
315 - (float)_calculatePrintHeight;
316 - (DOMRange *)_selectedRange;
317 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
318 - (NSView *)_hitViewForEvent:(NSEvent *)event;
319 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
320 - (DOMRange *)_documentRange;
321 - (void)_setMouseDownEvent:(NSEvent *)event;
322 - (WebHTMLView *)_topHTMLView;
323 - (BOOL)_isTopHTMLView;
324 - (void)_web_setPrintingModeRecursive;
325 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
326 - (void)_web_clearPrintingModeRecursive;
329 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
330 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize;
333 @class NSTextInputContext;
334 @interface NSResponder (AppKitDetails)
335 - (NSTextInputContext *)inputContext;
338 @interface NSObject (NSTextInputContextDetails)
339 - (BOOL)wantsToHandleMouseEvents;
340 - (BOOL)handleMouseEvent:(NSEvent *)event;
343 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
344 - (void)_updateSelectionForInputManager;
347 @interface WebHTMLView (WebEditingStyleSupport)
348 - (DOMCSSStyleDeclaration *)_emptyStyle;
349 - (NSString *)_colorAsString:(NSColor *)color;
352 @interface NSView (WebHTMLViewFileInternal)
353 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
356 @interface NSMutableDictionary (WebHTMLViewFileInternal)
357 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
360 // Handles the complete: text command
361 @interface WebTextCompleteController : NSObject <NSTableViewDelegate, NSTableViewDataSource> {
364 NSWindow *_popupWindow;
365 NSTableView *_tableView;
366 NSArray *_completions;
367 NSString *_originalString;
370 - (id)initWithHTMLView:(WebHTMLView *)view;
371 - (void)doCompletion;
372 - (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft;
373 - (BOOL)popupWindowIsOpen;
374 - (BOOL)filterKeyDown:(NSEvent *)event;
375 - (void)_reflectSelection;
378 struct WebHTMLViewInterpretKeyEventsParameters {
379 KeyboardEvent* event;
380 BOOL eventWasHandled;
381 BOOL shouldSaveCommand;
382 // The Input Method may consume an event and not tell us, in
383 // which case we should not bubble the event up the DOM
387 @interface WebHTMLViewPrivate : NSObject {
391 BOOL needsToApplyStyles;
392 BOOL ignoringMouseDraggedEvents;
394 BOOL avoidingPrintOrphan;
395 BOOL observingMouseMovedNotifications;
396 BOOL observingSuperviewNotifications;
397 BOOL observingWindowNotifications;
398 BOOL resigningFirstResponder;
401 BOOL subviewsSetAside;
403 #if USE(ACCELERATED_COMPOSITING)
404 NSView *layerHostingView;
407 NSEvent *mouseDownEvent; // Kept after handling the event.
408 BOOL handlingMouseDownEvent;
409 NSEvent *keyDownEvent; // Kept after handling the event.
411 NSSize lastLayoutSize;
413 NSPoint lastScrollPosition;
415 WebPluginController *pluginController;
418 NSToolTipTag lastToolTipTag;
419 id trackingRectOwner;
420 void *trackingRectUserData;
422 NSTimer *autoscrollTimer;
423 NSEvent *autoscrollTriggerEvent;
427 NSMutableDictionary *highlighters;
429 #ifdef BUILDING_ON_TIGER
430 BOOL nextResponderDisabledOnce;
433 WebTextCompleteController *compController;
435 BOOL transparentBackground;
437 WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters;
440 WebDataSource *dataSource;
441 WebCore::CachedImage* promisedDragTIFFDataSource;
443 CFRunLoopTimerRef updateFocusedAndActiveStateTimer;
444 CFRunLoopTimerRef updateMouseoverTimer;
446 SEL selectorForDoCommandBySelector;
449 BOOL enumeratingSubviews;
455 static NSCellStateValue kit(TriState state)
465 ASSERT_NOT_REACHED();
469 @implementation WebHTMLViewPrivate
473 JSC::initializeThreading();
474 #ifndef BUILDING_ON_TIGER
475 WebCoreObjCFinalizeOnMainThread(self);
478 if (!oldSetCursorIMP) {
479 #ifdef BUILDING_ON_TIGER
480 Method setCursorMethod = class_getInstanceMethod([NSCursor class], @selector(set));
482 Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
484 ASSERT(setCursorMethod);
486 oldSetCursorIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
487 ASSERT(oldSetCursorIMP);
490 #ifdef BUILDING_ON_TIGER
491 if (!oldResetCursorRectsIMP) {
492 Method resetCursorRectsMethod = class_getInstanceMethod([NSWindow class], @selector(resetCursorRects));
493 ASSERT(resetCursorRectsMethod);
494 oldResetCursorRectsIMP = method_setImplementation(resetCursorRectsMethod, (IMP)resetCursorRects);
495 ASSERT(oldResetCursorRectsIMP);
503 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self))
506 ASSERT(!autoscrollTimer);
507 ASSERT(!autoscrollTriggerEvent);
508 ASSERT(!updateFocusedAndActiveStateTimer);
509 ASSERT(!updateMouseoverTimer);
511 [mouseDownEvent release];
512 [keyDownEvent release];
513 [pluginController release];
515 [compController release];
516 [dataSource release];
517 [highlighters release];
518 if (promisedDragTIFFDataSource)
519 promisedDragTIFFDataSource->removeClient(promisedDataClient());
526 ASSERT_MAIN_THREAD();
528 if (promisedDragTIFFDataSource)
529 promisedDragTIFFDataSource->removeClient(promisedDataClient());
536 [mouseDownEvent release];
537 [keyDownEvent release];
538 [pluginController release];
540 [compController release];
541 [dataSource release];
542 [highlighters release];
543 if (promisedDragTIFFDataSource)
544 promisedDragTIFFDataSource->removeClient(promisedDataClient());
546 mouseDownEvent = nil;
548 pluginController = nil;
550 compController = nil;
553 promisedDragTIFFDataSource = 0;
555 #if USE(ACCELERATED_COMPOSITING)
556 layerHostingView = nil;
562 @implementation WebHTMLView (WebHTMLViewFileInternal)
564 - (DOMRange *)_documentRange
566 return [[[self _frame] DOMDocument] _documentRange];
569 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
571 NSEnumerator *enumerator = [paths objectEnumerator];
574 while ((path = [enumerator nextObject]) != nil) {
575 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
576 if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
583 - (WebDataSource *)_dataSource
585 return _private->dataSource;
588 - (WebView *)_webView
590 return [_private->dataSource _webView];
593 - (WebFrameView *)_frameView
595 return [[_private->dataSource webFrame] frameView];
598 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
600 DOMDocumentFragment *fragment;
601 NSEnumerator *enumerator = [paths objectEnumerator];
602 NSMutableArray *domNodes = [[NSMutableArray alloc] init];
605 while ((path = [enumerator nextObject]) != nil) {
606 // Non-image file types; _web_userVisibleString is appropriate here because this will
607 // be pasted as visible text.
608 NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
609 [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
612 fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes];
616 return [fragment firstChild] != nil ? fragment : nil;
619 + (NSArray *)_excludedElementsForAttributedStringConversion
621 static NSArray *elements = nil;
622 if (elements == nil) {
623 elements = [[NSArray alloc] initWithObjects:
624 // Omit style since we want style to be inline so the fragment can be easily inserted.
626 // Omit xml so the result is not XHTML.
628 // Omit tags that will get stripped when converted to a fragment anyway.
629 @"doctype", @"html", @"head", @"body",
630 // Omit deprecated tags.
631 @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
632 // Omit object so no file attachments are part of the fragment.
639 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
641 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
642 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
644 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
645 CFRelease(UUIDString);
650 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
651 inContext:(DOMRange *)context
652 allowPlainText:(BOOL)allowPlainText
654 NSArray *types = [pasteboard types];
655 DOMDocumentFragment *fragment = nil;
657 if ([types containsObject:WebArchivePboardType] &&
658 (fragment = [self _documentFragmentFromPasteboard:pasteboard
659 forType:WebArchivePboardType
664 if ([types containsObject:NSFilenamesPboardType] &&
665 (fragment = [self _documentFragmentFromPasteboard:pasteboard
666 forType:NSFilenamesPboardType
671 if ([types containsObject:NSHTMLPboardType] &&
672 (fragment = [self _documentFragmentFromPasteboard:pasteboard
673 forType:NSHTMLPboardType
678 if ([types containsObject:NSRTFPboardType] &&
679 (fragment = [self _documentFragmentFromPasteboard:pasteboard
680 forType:NSRTFPboardType
685 if ([types containsObject:NSRTFDPboardType] &&
686 (fragment = [self _documentFragmentFromPasteboard:pasteboard
687 forType:NSRTFDPboardType
692 if ([types containsObject:NSTIFFPboardType] &&
693 (fragment = [self _documentFragmentFromPasteboard:pasteboard
694 forType:NSTIFFPboardType
699 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
700 if ([types containsObject:NSPICTPboardType] &&
701 (fragment = [self _documentFragmentFromPasteboard:pasteboard
702 forType:NSPICTPboardType
708 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
709 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
710 if ([types containsObject:(NSString*)kUTTypePNG] &&
711 (fragment = [self _documentFragmentFromPasteboard:pasteboard
712 forType:(NSString*)kUTTypePNG
717 if ([types containsObject:NSURLPboardType] &&
718 (fragment = [self _documentFragmentFromPasteboard:pasteboard
719 forType:NSURLPboardType
724 if (allowPlainText && [types containsObject:NSStringPboardType] &&
725 (fragment = [self _documentFragmentFromPasteboard:pasteboard
726 forType:NSStringPboardType
735 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
737 NSArray *types = [pasteboard types];
739 if ([types containsObject:NSStringPboardType])
740 return [pasteboard stringForType:NSStringPboardType];
742 NSAttributedString *attributedString = nil;
745 if ([types containsObject:NSRTFDPboardType])
746 attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
747 if (attributedString == nil && [types containsObject:NSRTFPboardType])
748 attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
749 if (attributedString != nil) {
750 string = [[attributedString string] copy];
751 [attributedString release];
752 return [string autorelease];
755 if ([types containsObject:NSFilenamesPboardType]) {
756 string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
763 if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
764 string = [URL _web_userVisibleString];
765 if ([string length] > 0)
772 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
774 DOMRange *range = [self _selectedRange];
775 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard
776 inContext:range allowPlainText:allowPlainText];
777 WebFrame *frame = [self _frame];
778 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[self _selectedRange] givenAction:WebViewInsertActionPasted]) {
779 [frame _replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:NO];
783 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
785 NSString *text = [self _plainTextFromPasteboard:pasteboard];
786 if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
787 [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
790 - (void)_removeMouseMovedObserverUnconditionally
792 if (!_private || !_private->observingMouseMovedNotifications)
795 [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil];
796 _private->observingMouseMovedNotifications = false;
799 - (void)_removeSuperviewObservers
801 if (!_private || !_private->observingSuperviewNotifications)
804 NSView *superview = [self superview];
805 if (!superview || ![self window])
808 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
809 [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview];
810 [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview];
812 _private->observingSuperviewNotifications = false;
815 - (void)_removeWindowObservers
817 if (!_private->observingWindowNotifications)
820 NSWindow *window = [self window];
824 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
825 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
826 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
827 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window];
828 [notificationCenter removeObserver:self name:WKWindowWillOrderOnScreenNotification() object:window];
830 _private->observingWindowNotifications = false;
833 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
835 WebView *webView = [self _webView];
836 DOMNode *child = [fragment firstChild];
837 if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
838 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
839 return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
842 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
844 WebView *webView = [self _webView];
845 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
848 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
850 return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
853 // Calculate the vertical size of the view that fits on a single page
854 - (float)_calculatePrintHeight
856 // Obtain the print info object for the current operation
857 NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
859 // Calculate the page height in points
860 NSSize paperSize = [pi paperSize];
861 return paperSize.height - [pi topMargin] - [pi bottomMargin];
864 - (DOMRange *)_selectedRange
866 Frame* coreFrame = core([self _frame]);
867 return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil;
870 - (BOOL)_shouldDeleteRange:(DOMRange *)range
872 Frame* coreFrame = core([self _frame]);
873 return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
876 - (NSView *)_hitViewForEvent:(NSEvent *)event
878 // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.
879 // Callers of this method, however, want to query the deepest view instead.
880 forceNSViewHitTest = YES;
881 NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
882 forceNSViewHitTest = NO;
886 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
888 // Put HTML on the pasteboard.
889 if ([types containsObject:WebArchivePboardType]) {
890 if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) {
891 if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0)
892 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType];
896 // Put the attributed string on the pasteboard (RTF/RTFD format).
897 if ([types containsObject:NSRTFDPboardType]) {
898 if (attributedString == nil) {
899 attributedString = [self selectedAttributedString];
901 NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
902 [pasteboard setData:RTFDData forType:NSRTFDPboardType];
904 if ([types containsObject:NSRTFPboardType]) {
905 if (attributedString == nil) {
906 attributedString = [self selectedAttributedString];
908 if ([attributedString containsAttachments]) {
909 attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
911 NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
912 [pasteboard setData:RTFData forType:NSRTFPboardType];
915 // Put plain string on the pasteboard.
916 if ([types containsObject:NSStringPboardType]) {
917 // Map to a plain old space because this is better for source code, other browsers do it,
918 // and because HTML forces you to do this any time you want two spaces in a row.
919 NSMutableString *s = [[self selectedString] mutableCopy];
920 const unichar NonBreakingSpaceCharacter = 0xA0;
921 NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
922 [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
923 [pasteboard setString:s forType:NSStringPboardType];
927 if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
928 [pasteboard setData:nil forType:WebSmartPastePboardType];
932 - (void)_setMouseDownEvent:(NSEvent *)event
934 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
936 if (event == _private->mouseDownEvent)
940 [_private->mouseDownEvent release];
941 _private->mouseDownEvent = event;
944 - (void)_cancelUpdateFocusedAndActiveStateTimer
946 if (_private->updateFocusedAndActiveStateTimer) {
947 CFRunLoopTimerInvalidate(_private->updateFocusedAndActiveStateTimer);
948 CFRelease(_private->updateFocusedAndActiveStateTimer);
949 _private->updateFocusedAndActiveStateTimer = NULL;
953 - (void)_cancelUpdateMouseoverTimer
955 if (_private->updateMouseoverTimer) {
956 CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
957 CFRelease(_private->updateMouseoverTimer);
958 _private->updateMouseoverTimer = NULL;
962 - (WebHTMLView *)_topHTMLView
964 // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
965 WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
967 ASSERT([view isKindOfClass:[WebHTMLView class]]);
971 - (BOOL)_isTopHTMLView
973 // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
974 return self == [self _topHTMLView];
977 - (void)_web_setPrintingModeRecursive
979 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
982 _private->enumeratingSubviews = YES;
985 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
987 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
989 unsigned count = [descendantWebHTMLViews count];
990 for (unsigned i = 0; i < count; ++i)
991 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
993 [descendantWebHTMLViews release];
996 _private->enumeratingSubviews = NO;
1000 - (void)_web_clearPrintingModeRecursive
1002 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
1005 _private->enumeratingSubviews = YES;
1008 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1010 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1012 unsigned count = [descendantWebHTMLViews count];
1013 for (unsigned i = 0; i < count; ++i)
1014 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
1016 [descendantWebHTMLViews release];
1019 _private->enumeratingSubviews = NO;
1023 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize
1025 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
1028 _private->enumeratingSubviews = YES;
1031 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1033 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1035 unsigned count = [descendantWebHTMLViews count];
1036 for (unsigned i = 0; i < count; ++i)
1037 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
1039 [descendantWebHTMLViews release];
1042 _private->enumeratingSubviews = NO;
1048 @implementation WebHTMLView (WebPrivate)
1050 + (NSArray *)supportedMIMETypes
1052 return [WebHTMLRepresentation supportedMIMETypes];
1055 + (NSArray *)supportedImageMIMETypes
1057 return [WebHTMLRepresentation supportedImageMIMETypes];
1060 + (NSArray *)supportedNonImageMIMETypes
1062 return [WebHTMLRepresentation supportedNonImageMIMETypes];
1065 + (NSArray *)unsupportedTextMIMETypes
1067 return [NSArray arrayWithObjects:
1068 @"text/calendar", // iCal
1070 @"text/x-vcalendar",
1072 @"text/vcard", // vCard
1075 @"text/ldif", // Netscape Address Book
1076 @"text/qif", // Quicken
1078 @"text/x-csv", // CSV (for Address Book and Microsoft Outlook)
1079 @"text/x-vcf", // vCard type used in Sun affinity app
1080 @"text/rtf", // Rich Text Format
1084 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
1086 // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged
1087 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1088 location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
1089 modifierFlags:[flagsChangedEvent modifierFlags]
1090 timestamp:[flagsChangedEvent timestamp]
1091 windowNumber:[flagsChangedEvent windowNumber]
1092 context:[flagsChangedEvent context]
1093 eventNumber:0 clickCount:0 pressure:0];
1095 // Pretend it's a mouse move.
1096 [[NSNotificationCenter defaultCenter]
1097 postNotificationName:WKMouseMovedNotification() object:self
1098 userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
1103 // This method exists to maintain compatibility with Leopard's Dictionary.app, since it
1104 // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:.
1105 // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160>
1106 return [self _frame];
1109 - (void)_updateMouseoverWithFakeEvent
1111 [self _cancelUpdateMouseoverTimer];
1113 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1114 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1115 modifierFlags:[[NSApp currentEvent] modifierFlags]
1116 timestamp:[NSDate timeIntervalSinceReferenceDate]
1117 windowNumber:[[self window] windowNumber]
1118 context:[[NSApp currentEvent] context]
1119 eventNumber:0 clickCount:0 pressure:0];
1121 [self _updateMouseoverWithEvent:fakeEvent];
1124 static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
1126 WebHTMLView *view = (WebHTMLView *)info;
1128 [view _updateMouseoverWithFakeEvent];
1131 - (void)_frameOrBoundsChanged
1133 if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
1134 [self setNeedsLayout:YES];
1135 [self setNeedsDisplay:YES];
1136 [_private->compController endRevertingChange:NO moveLeft:NO];
1139 NSPoint origin = [[self superview] bounds].origin;
1140 if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
1141 if (Frame* coreFrame = core([self _frame]))
1142 coreFrame->eventHandler()->sendScrollEvent();
1143 [_private->compController endRevertingChange:NO moveLeft:NO];
1145 WebView *webView = [self _webView];
1146 [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
1148 _private->lastScrollPosition = origin;
1150 if ([self window] && !_private->closed && !_private->updateMouseoverTimer) {
1151 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
1153 // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading
1154 // and scrolling rapidly back to back.
1155 _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0,
1156 _updateMouseoverTimerCallback, &context);
1157 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode);
1161 - (void)_setAsideSubviews
1163 ASSERT(!_private->subviewsSetAside);
1164 ASSERT(_private->savedSubviews == nil);
1165 _private->savedSubviews = _subviews;
1166 #if USE(ACCELERATED_COMPOSITING)
1167 // We need to keep the layer-hosting view in the subviews, otherwise the layers flash.
1168 if (_private->layerHostingView) {
1169 NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil];
1170 _subviews = newSubviews;
1176 _private->subviewsSetAside = YES;
1179 - (void)_restoreSubviews
1181 ASSERT(_private->subviewsSetAside);
1182 #if USE(ACCELERATED_COMPOSITING)
1183 if (_private->layerHostingView) {
1184 [_subviews release];
1185 _subviews = _private->savedSubviews;
1187 ASSERT(_subviews == nil);
1188 _subviews = _private->savedSubviews;
1191 ASSERT(_subviews == nil);
1192 _subviews = _private->savedSubviews;
1194 _private->savedSubviews = nil;
1195 _private->subviewsSetAside = NO;
1200 - (void)didAddSubview:(NSView *)subview
1202 if (_private->enumeratingSubviews)
1203 LOG(View, "A view of class %s was added during subview enumeration for layout or printing mode change. This view might paint without first receiving layout.", object_getClassName([subview class]));
1206 - (void)willRemoveSubview:(NSView *)subview
1208 // Have to null-check _private, since this can be called via -dealloc when
1209 // cleaning up the the layerHostingView.
1210 if (_private && _private->enumeratingSubviews)
1211 LOG(View, "A view of class %s was removed during subview enumeration for layout or printing mode change. We will still do layout or the printing mode change even though this view is no longer in the view hierarchy.", object_getClassName([subview class]));
1216 #ifdef BUILDING_ON_TIGER
1218 // This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
1219 // That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty
1220 // before doing it. As a compromise, when we're opaque we do the layout only when actually asked to
1221 // draw, but when we're transparent we do the layout at this stage so views behind us know that they
1222 // need to be redrawn (in case the layout causes some things to get dirtied).
1223 - (void)_propagateDirtyRectsToOpaqueAncestors
1225 if (![[self _webView] drawsBackground])
1226 [self _web_layoutIfNeededRecursive];
1227 [super _propagateDirtyRectsToOpaqueAncestors];
1232 - (void)viewWillDraw
1234 // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1235 // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1236 // once the FIXME in _isTopHTMLView is fixed.
1237 if (_private->dataSource && [self _isTopHTMLView])
1238 [self _web_layoutIfNeededRecursive];
1239 [super viewWillDraw];
1244 // Don't let AppKit even draw subviews. We take care of that.
1245 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1247 // This helps when we print as part of a larger print process.
1248 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1249 BOOL wasInPrintingMode = _private->printing;
1250 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1252 if (!wasInPrintingMode)
1253 [self _web_setPrintingModeRecursive];
1254 #ifndef BUILDING_ON_TIGER
1256 [self _web_layoutIfNeededRecursive];
1258 } else if (wasInPrintingMode)
1259 [self _web_clearPrintingModeRecursive];
1261 #ifdef BUILDING_ON_TIGER
1263 // Because Tiger does not have viewWillDraw we need to do layout here.
1264 [self _web_layoutIfNeededRecursive];
1265 [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
1269 [self _setAsideSubviews];
1270 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1271 [self _restoreSubviews];
1273 if (wasInPrintingMode != isPrinting) {
1274 if (wasInPrintingMode)
1275 [self _web_setPrintingModeRecursive];
1277 [self _web_clearPrintingModeRecursive];
1281 // Don't let AppKit even draw subviews. We take care of that.
1282 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1284 BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1286 BOOL wasInPrintingMode = _private->printing;
1287 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1289 if (needToSetAsideSubviews) {
1290 // This helps when we print as part of a larger print process.
1291 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1293 if (!wasInPrintingMode)
1294 [self _web_setPrintingModeRecursive];
1295 #ifndef BUILDING_ON_TIGER
1297 [self _web_layoutIfNeededRecursive];
1299 } else if (wasInPrintingMode)
1300 [self _web_clearPrintingModeRecursive];
1302 #ifdef BUILDING_ON_TIGER
1304 // Because Tiger does not have viewWillDraw we need to do layout here.
1305 NSRect boundsBeforeLayout = [self bounds];
1306 if (!NSIsEmptyRect(visRect))
1307 [self _web_layoutIfNeededRecursive];
1309 // If layout changes the view's bounds, then we need to recompute the visRect.
1310 // That's because the visRect passed to us was based on the bounds at the time
1311 // we were called. This method is only displayed to draw "all", so it's safe
1312 // to just call visibleRect to compute the entire rectangle.
1313 if (!NSEqualRects(boundsBeforeLayout, [self bounds]))
1314 visRect = [self visibleRect];
1318 [self _setAsideSubviews];
1321 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1323 if (needToSetAsideSubviews) {
1324 if (wasInPrintingMode != isPrinting) {
1325 if (wasInPrintingMode)
1326 [self _web_setPrintingModeRecursive];
1328 [self _web_clearPrintingModeRecursive];
1331 [self _restoreSubviews];
1335 // Don't let AppKit even draw subviews. We take care of that.
1336 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView
1338 #ifdef BUILDING_ON_TIGER
1339 // Because Tiger does not have viewWillDraw we need to do layout here.
1340 [self _web_layoutIfNeededRecursive];
1343 [self _setAsideSubviews];
1344 [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView];
1345 [self _restoreSubviews];
1348 - (BOOL)_insideAnotherHTMLView
1350 return self != [self _topHTMLView];
1353 - (NSView *)hitTest:(NSPoint)point
1355 // WebHTMLView objects handle all events for objects inside them.
1356 // To get those events, we prevent hit testing from AppKit.
1358 // But there are three exceptions to this:
1359 // 1) For right mouse clicks and control clicks we don't yet have an implementation
1360 // that works for nested views, so we let the hit testing go through the
1361 // standard NSView code path (needs to be fixed, see bug 4361618).
1362 // 2) Java depends on doing a hit test inside it's mouse moved handling,
1363 // so we let the hit testing go through the standard NSView code path
1364 // when the current event is a mouse move (except when we are calling
1365 // from _updateMouseoverWithEvent, so we have to use a global,
1366 // forceWebHTMLViewHitTest, for that)
1367 // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1368 // both need to figure out which view to check with inside the WebHTMLView.
1369 // They use a global to change the behavior of hitTest: so they can get the
1370 // right view. The global is forceNSViewHitTest and the method they use to
1371 // do the hit testing is _hitViewForEvent:. (But this does not work correctly
1372 // when there is HTML overlapping the view, see bug 4361626)
1373 // 4) NSAccessibilityHitTest relies on this for checking the cursor position.
1374 // Our check for that is whether the event is NSFlagsChanged. This works
1375 // for VoiceOver's cntl-opt-f5 command (move focus to item under cursor)
1376 // and Dictionary's cmd-cntl-D (open dictionary popup for item under cursor).
1377 // This is of course a hack.
1379 BOOL captureHitsOnSubviews;
1380 if (forceNSViewHitTest)
1381 captureHitsOnSubviews = NO;
1382 else if (forceWebHTMLViewHitTest)
1383 captureHitsOnSubviews = YES;
1385 NSEvent *event = [[self window] currentEvent];
1386 captureHitsOnSubviews = !([event type] == NSMouseMoved
1387 || [event type] == NSRightMouseDown
1388 || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1389 || [event type] == NSFlagsChanged);
1392 if (!captureHitsOnSubviews)
1393 return [super hitTest:point];
1394 if ([[self superview] mouse:point inRect:[self frame]])
1399 - (void)_clearLastHitViewIfSelf
1401 if (lastHitView == self)
1405 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1407 ASSERT(_private->trackingRectOwner == nil);
1408 _private->trackingRectOwner = owner;
1409 _private->trackingRectUserData = data;
1410 return TRACKING_RECT_TAG;
1413 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1415 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1416 ASSERT(_private->trackingRectOwner == nil);
1417 _private->trackingRectOwner = owner;
1418 _private->trackingRectUserData = data;
1419 return TRACKING_RECT_TAG;
1422 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1425 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1426 ASSERT(_private->trackingRectOwner == nil);
1427 _private->trackingRectOwner = owner;
1428 _private->trackingRectUserData = userDataList[0];
1429 trackingNums[0] = TRACKING_RECT_TAG;
1432 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1437 if (_private && (tag == TRACKING_RECT_TAG)) {
1438 _private->trackingRectOwner = nil;
1442 if (_private && (tag == _private->lastToolTipTag)) {
1443 [super removeTrackingRect:tag];
1444 _private->lastToolTipTag = 0;
1448 // If any other tracking rect is being removed, we don't know how it was created
1449 // and it's possible there's a leak involved (see 3500217)
1450 ASSERT_NOT_REACHED();
1453 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1456 for (i = 0; i < count; ++i) {
1460 ASSERT(tag == TRACKING_RECT_TAG);
1461 if (_private != nil) {
1462 _private->trackingRectOwner = nil;
1467 - (void)_sendToolTipMouseExited
1469 // Nothing matters except window, trackingNumber, and userData.
1470 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1471 location:NSMakePoint(0, 0)
1474 windowNumber:[[self window] windowNumber]
1477 trackingNumber:TRACKING_RECT_TAG
1478 userData:_private->trackingRectUserData];
1479 [_private->trackingRectOwner mouseExited:fakeEvent];
1482 - (void)_sendToolTipMouseEntered
1484 // Nothing matters except window, trackingNumber, and userData.
1485 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1486 location:NSMakePoint(0, 0)
1489 windowNumber:[[self window] windowNumber]
1492 trackingNumber:TRACKING_RECT_TAG
1493 userData:_private->trackingRectUserData];
1494 [_private->trackingRectOwner mouseEntered:fakeEvent];
1497 - (void)_setToolTip:(NSString *)string
1499 NSString *toolTip = [string length] == 0 ? nil : string;
1500 NSString *oldToolTip = _private->toolTip;
1501 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1505 [self _sendToolTipMouseExited];
1506 [oldToolTip release];
1508 _private->toolTip = [toolTip copy];
1510 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1511 [self removeAllToolTips];
1512 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1513 _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1514 [self _sendToolTipMouseEntered];
1518 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1520 return [[_private->toolTip copy] autorelease];
1523 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1525 if (_private->closed)
1528 NSView *contentView = [[event window] contentView];
1529 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1531 forceWebHTMLViewHitTest = YES;
1532 NSView *hitView = [contentView hitTest:locationForHitTest];
1533 forceWebHTMLViewHitTest = NO;
1535 WebHTMLView *view = nil;
1536 if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
1537 view = (WebHTMLView *)hitView;
1542 if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1543 // If we are moving out of a view (or frame), let's pretend the mouse moved
1544 // all the way out of that view. But we have to account for scrolling, because
1545 // khtml doesn't understand our clipping.
1546 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1547 float yScroll = visibleRect.origin.y;
1548 float xScroll = visibleRect.origin.x;
1550 event = [NSEvent mouseEventWithType:NSMouseMoved
1551 location:NSMakePoint(-1 - xScroll, -1 - yScroll )
1552 modifierFlags:[[NSApp currentEvent] modifierFlags]
1553 timestamp:[NSDate timeIntervalSinceReferenceDate]
1554 windowNumber:[[view window] windowNumber]
1555 context:[[NSApp currentEvent] context]
1556 eventNumber:0 clickCount:0 pressure:0];
1557 if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1558 lastHitCoreFrame->eventHandler()->mouseMoved(event);
1564 if (Frame* coreFrame = core([view _frame]))
1565 coreFrame->eventHandler()->mouseMoved(event);
1571 // keep in sync with WebPasteboardHelper::insertablePasteboardTypes
1572 + (NSArray *)_insertablePasteboardTypes
1574 static NSArray *types = nil;
1576 types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType,
1577 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
1580 NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
1586 + (NSArray *)_selectionPasteboardTypes
1588 // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1589 return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1592 - (NSImage *)_dragImageForURL:(NSString*)urlString withLabel:(NSString*)label
1594 BOOL drawURLString = YES;
1595 BOOL clipURLString = NO, clipLabelString = NO;
1602 NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
1603 toHaveTrait:NSBoldFontMask];
1604 NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
1606 labelSize.width = [label _web_widthWithFont: labelFont];
1607 labelSize.height = [labelFont ascender] - [labelFont descender];
1608 if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
1609 labelSize.width = MAX_DRAG_LABEL_WIDTH;
1610 clipLabelString = YES;
1613 NSSize imageSize, urlStringSize;
1614 imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2.0f;
1615 imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2.0f;
1616 if (drawURLString) {
1617 urlStringSize.width = [urlString _web_widthWithFont: urlFont];
1618 urlStringSize.height = [urlFont ascender] - [urlFont descender];
1619 imageSize.height += urlStringSize.height;
1620 if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
1621 imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2.0f, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
1622 clipURLString = YES;
1624 imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2.0f, urlStringSize.width + DRAG_LABEL_BORDER_X * 2.0f);
1627 NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
1628 [dragImage lockFocus];
1630 [[NSColor colorWithDeviceRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
1632 // Drag a rectangle with rounded corners/
1633 NSBezierPath *path = [NSBezierPath bezierPath];
1634 [path appendBezierPathWithOvalInRect: NSMakeRect(0.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1635 [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1636 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1637 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1639 [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0.0f, imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height)];
1640 [path appendBezierPathWithRect: NSMakeRect(0.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1641 [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS - 20.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 20.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1644 NSColor *topColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.75f];
1645 NSColor *bottomColor = [NSColor colorWithDeviceWhite:1.0f alpha:0.5f];
1646 if (drawURLString) {
1648 urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:urlFont];
1650 [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender])
1651 withTopColor:topColor bottomColor:bottomColor font:urlFont];
1654 if (clipLabelString)
1655 label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:labelFont];
1656 [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
1657 withTopColor:topColor bottomColor:bottomColor font:labelFont];
1659 [dragImage unlockFocus];
1664 - (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
1666 NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
1668 NSString *label = [element objectForKey: WebElementLinkLabelKey];
1669 NSString *urlString = [linkURL _web_userVisibleString];
1670 return [self _dragImageForURL:urlString withLabel:label];
1673 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1675 [self setPromisedDragTIFFDataSource:0];
1678 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1680 if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1681 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1682 [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1684 } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1685 if (Image* image = [self promisedDragTIFFDataSource]->image())
1686 [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1687 [self setPromisedDragTIFFDataSource:0];
1691 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
1693 [self autoscroll:event];
1694 [self _startAutoscrollTimer:event];
1697 - (WebPluginController *)_pluginController
1699 return _private->pluginController;
1702 - (void)_layoutForPrinting
1704 // Set printing mode temporarily so we can adjust the size of the view. This will allow
1705 // AppKit's pagination code to use the correct height for the page content. Leaving printing
1706 // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1707 // turn it off again after adjusting the size.
1708 [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1709 [self _web_clearPrintingModeRecursive];
1712 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1714 if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
1716 *beforeString = nil;
1722 [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
1725 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
1727 return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
1730 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1732 if (_private->autoscrollTimer == nil) {
1733 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1734 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1735 _private->autoscrollTriggerEvent = [triggerEvent retain];
1739 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1740 // We can't remove this yet because it's still in use by Mail.
1741 - (NSRect)_selectionRect
1743 return [self selectionRect];
1746 - (void)_stopAutoscrollTimer
1748 NSTimer *timer = _private->autoscrollTimer;
1749 _private->autoscrollTimer = nil;
1750 [_private->autoscrollTriggerEvent release];
1751 _private->autoscrollTriggerEvent = nil;
1758 // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1759 // a mouse up event.
1760 BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);
1762 [self _stopAutoscrollTimer];
1766 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1767 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1768 modifierFlags:[[NSApp currentEvent] modifierFlags]
1769 timestamp:[NSDate timeIntervalSinceReferenceDate]
1770 windowNumber:[[self window] windowNumber]
1771 context:[[NSApp currentEvent] context]
1772 eventNumber:0 clickCount:0 pressure:0];
1773 [self mouseDragged:fakeEvent];
1778 Frame* coreFrame = core([self _frame]);
1779 return coreFrame && coreFrame->editor()->canEdit();
1782 - (BOOL)_canEditRichly
1784 Frame* coreFrame = core([self _frame]);
1785 return coreFrame && coreFrame->editor()->canEditRichly();
1788 - (BOOL)_canAlterCurrentSelection
1790 return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1793 - (BOOL)_hasSelection
1795 Frame* coreFrame = core([self _frame]);
1796 return coreFrame && coreFrame->selection()->isRange();
1799 - (BOOL)_hasSelectionOrInsertionPoint
1801 Frame* coreFrame = core([self _frame]);
1802 return coreFrame && coreFrame->selection()->isCaretOrRange();
1805 - (BOOL)_hasInsertionPoint
1807 Frame* coreFrame = core([self _frame]);
1808 return coreFrame && coreFrame->selection()->isCaret();
1813 Frame* coreFrame = core([self _frame]);
1814 return coreFrame && coreFrame->selection()->isContentEditable();
1817 - (BOOL)_transparentBackground
1819 return _private->transparentBackground;
1822 - (void)_setTransparentBackground:(BOOL)f
1824 _private->transparentBackground = f;
1827 - (NSImage *)_selectionDraggingImage
1829 if ([self _hasSelection]) {
1830 NSImage *dragImage = core([self _frame])->selectionImage();
1831 [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1837 - (NSRect)_selectionDraggingRect
1839 // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1840 return [self selectionRect];
1843 - (DOMNode *)_insertOrderedList
1845 Frame* coreFrame = core([self _frame]);
1846 return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
1849 - (DOMNode *)_insertUnorderedList
1851 Frame* coreFrame = core([self _frame]);
1852 return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
1855 - (BOOL)_canIncreaseSelectionListLevel
1857 Frame* coreFrame = core([self _frame]);
1858 return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
1861 - (BOOL)_canDecreaseSelectionListLevel
1863 Frame* coreFrame = core([self _frame]);
1864 return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
1867 - (DOMNode *)_increaseSelectionListLevel
1869 Frame* coreFrame = core([self _frame]);
1870 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
1873 - (DOMNode *)_increaseSelectionListLevelOrdered
1875 Frame* coreFrame = core([self _frame]);
1876 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
1879 - (DOMNode *)_increaseSelectionListLevelUnordered
1881 Frame* coreFrame = core([self _frame]);
1882 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
1885 - (void)_decreaseSelectionListLevel
1887 Frame* coreFrame = core([self _frame]);
1889 coreFrame->editor()->decreaseSelectionListLevel();
1892 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1894 if (!_private->highlighters)
1895 _private->highlighters = [[NSMutableDictionary alloc] init];
1896 [_private->highlighters setObject:highlighter forKey:type];
1899 - (void)_removeHighlighterOfType:(NSString*)type
1901 [_private->highlighters removeObjectForKey:type];
1904 - (void)_updateFocusedAndActiveState
1906 [self _cancelUpdateFocusedAndActiveStateTimer];
1908 [[self _webView] _updateFocusedAndActiveStateForFrame:[self _frame]];
1911 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1913 ASSERT([self _hasSelection]);
1914 NSArray *types = [self pasteboardTypesForSelection];
1916 // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1917 NSAttributedString *attributedString = [self selectedAttributedString];
1918 NSMutableArray *mutableTypes = nil;
1919 if (![attributedString containsAttachments]) {
1920 mutableTypes = [types mutableCopy];
1921 [mutableTypes removeObject:NSRTFDPboardType];
1922 types = mutableTypes;
1925 [pasteboard declareTypes:types owner:[self _topHTMLView]];
1926 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1927 [mutableTypes release];
1932 // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing
1933 // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
1934 if (!_private || _private->closed)
1937 _private->closed = YES;
1939 [self _cancelUpdateMouseoverTimer];
1940 [self _cancelUpdateFocusedAndActiveStateTimer];
1941 [self _clearLastHitViewIfSelf];
1942 [self _removeMouseMovedObserverUnconditionally];
1943 [self _removeWindowObservers];
1944 [self _removeSuperviewObservers];
1945 [_private->pluginController destroyAllPlugins];
1946 [_private->pluginController setDataSource:nil];
1947 // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1948 [self removeAllToolTips];
1950 #if USE(ACCELERATED_COMPOSITING)
1951 if (_private->layerHostingView)
1952 [[self _webView] _stoppedAcceleratedCompositingForFrame:[self _frame]];
1957 Page* page = core([self _webView]);
1959 page->dragController()->setDraggingImageURL(KURL());
1962 - (BOOL)_hasHTMLDocument
1964 Frame* coreFrame = core([self _frame]);
1967 Document* document = coreFrame->document();
1968 return document && document->isHTMLDocument();
1971 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1972 forType:(NSString *)pboardType
1973 inContext:(DOMRange *)context
1974 subresources:(NSArray **)subresources
1976 if (pboardType == WebArchivePboardType) {
1977 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1979 *subresources = [archive subresources];
1980 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1984 if (pboardType == NSFilenamesPboardType)
1985 return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1987 if (pboardType == NSHTMLPboardType) {
1988 NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1989 // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1990 if ([HTMLString hasPrefix:@"Version:"]) {
1991 NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
1992 if (range.location != NSNotFound)
1993 HTMLString = [HTMLString substringFromIndex:range.location];
1995 if ([HTMLString length] == 0)
1998 return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
2001 // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
2002 // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
2003 // FIXME: Remove this once bug 5052369 is fixed.
2004 if ([self _hasHTMLDocument] && pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType) {
2005 NSAttributedString *string = nil;
2006 if (pboardType == NSRTFDPboardType)
2007 string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
2009 string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
2013 NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
2014 [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
2015 self, @"WebResourceHandler", nil];
2018 BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
2019 if (!wasDeferringCallbacks)
2020 [[self _webView] setDefersCallbacks:YES];
2022 DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length])
2023 document:[[self _frame] DOMDocument]
2024 documentAttributes:documentAttributes
2029 NSEnumerator *e = [s objectEnumerator];
2031 while ((r = [e nextObject]))
2032 [[self _dataSource] addSubresource:r];
2034 if (!wasDeferringCallbacks)
2035 [[self _webView] setDefersCallbacks:NO];
2037 [documentAttributes release];
2041 if (pboardType == NSTIFFPboardType) {
2042 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
2043 URL:uniqueURLWithRelativePart(@"image.tiff")
2044 MIMEType:@"image/tiff"
2045 textEncodingName:nil
2047 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2051 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
2052 if (pboardType == NSPICTPboardType) {
2053 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
2054 URL:uniqueURLWithRelativePart(@"image.pict")
2055 MIMEType:@"image/pict"
2056 textEncodingName:nil
2058 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2063 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
2064 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
2065 if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) {
2066 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG]
2067 URL:uniqueURLWithRelativePart(@"image.png")
2068 MIMEType:@"image/png"
2069 textEncodingName:nil
2071 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2075 if (pboardType == NSURLPboardType) {
2076 NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
2077 DOMDocument* document = [[self _frame] DOMDocument];
2081 DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
2082 NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose.
2083 if ([URLString length] == 0)
2085 NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping];
2086 DOMText *text = [document createTextNode:URLTitleString];
2087 [anchor setHref:URLString];
2088 [anchor appendChild:text];
2089 DOMDocumentFragment *fragment = [document createDocumentFragment];
2090 [fragment appendChild:anchor];
2093 if (pboardType == NSStringPboardType)
2094 return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get());
2098 #if ENABLE(NETSCAPE_PLUGIN_API)
2099 - (void)_pauseNullEventsForAllNetscapePlugins
2101 NSArray *subviews = [self subviews];
2102 unsigned int subviewCount = [subviews count];
2103 unsigned int subviewIndex;
2105 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2106 NSView *subview = [subviews objectAtIndex:subviewIndex];
2107 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2108 [(WebBaseNetscapePluginView *)subview stopTimers];
2113 #if ENABLE(NETSCAPE_PLUGIN_API)
2114 - (void)_resumeNullEventsForAllNetscapePlugins
2116 NSArray *subviews = [self subviews];
2117 unsigned int subviewCount = [subviews count];
2118 unsigned int subviewIndex;
2120 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2121 NSView *subview = [subviews objectAtIndex:subviewIndex];
2122 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2123 [(WebBaseNetscapePluginView *)subview restartTimers];
2128 - (BOOL)_isUsingAcceleratedCompositing
2130 #if USE(ACCELERATED_COMPOSITING)
2131 return _private->layerHostingView != nil;
2139 @implementation NSView (WebHTMLViewFileInternal)
2141 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
2143 unsigned count = [_subviews count];
2144 for (unsigned i = 0; i < count; ++i) {
2145 NSView *child = [_subviews objectAtIndex:i];
2146 if ([child isKindOfClass:[WebHTMLView class]])
2147 [array addObject:child];
2148 [child _web_addDescendantWebHTMLViewsToArray:array];
2154 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
2156 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
2158 if (object == nil) {
2159 [self removeObjectForKey:key];
2161 [self setObject:object forKey:key];
2167 @interface NSString (WebHTMLViewFileInternal)
2168 - (BOOL)matchesExtensionEquivalent:(NSString *)extension;
2171 @implementation NSString (WebHTMLViewFileInternal)
2173 - (BOOL)matchesExtensionEquivalent:(NSString *)extension
2175 if ([self hasSuffix:extension])
2177 else if ([extension isEqualToString:@"jpeg"] && [self hasSuffix:@"jpg"])
2184 #ifdef BUILDING_ON_TIGER
2186 // The following is a workaround for
2187 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
2188 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
2189 // Since the category will be searched before the real class, we'll prevent the flag from being
2190 // set on the tool tip panel.
2192 @interface NSToolTipPanel : NSPanel
2195 @interface NSToolTipPanel (WebHTMLViewFileInternal)
2198 @implementation NSToolTipPanel (WebHTMLViewFileInternal)
2200 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
2202 // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
2209 @interface NSArray (WebHTMLView)
2210 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
2213 @implementation WebHTMLView
2217 [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes]
2218 returnTypes:[[self class] _insertablePasteboardTypes]];
2219 JSC::initializeThreading();
2220 #ifndef BUILDING_ON_TIGER
2221 WebCoreObjCFinalizeOnMainThread(self);
2225 - (id)initWithFrame:(NSRect)frame
2227 self = [super initWithFrame:frame];
2231 [self setFocusRingType:NSFocusRingTypeNone];
2233 // Make all drawing go through us instead of subviews.
2234 [self _setDrawsOwnDescendants:YES];
2236 _private = [[WebHTMLViewPrivate alloc] init];
2238 _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2239 _private->needsLayout = YES;
2246 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self))
2249 // We can't assert that close has already been called because
2250 // this view can be removed from it's superview, even though
2251 // it could be needed later, so close if needed.
2260 ASSERT_MAIN_THREAD();
2261 // We can't assert that close has already been called because
2262 // this view can be removed from it's superview, even though
2263 // it could be needed later, so close if needed.
2268 // Returns YES if the delegate returns YES (so we should do no more work).
2269 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2271 BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2272 _private->selectorForDoCommandBySelector = 0;
2273 if (callerAlreadyCalledDelegate)
2275 WebView *webView = [self _webView];
2276 return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2279 static String commandNameForSelector(SEL selector)
2281 // Change a few command names into ones supported by WebCore::Editor.
2282 // If this list gets too long we might decide we need to use a hash table.
2283 if (selector == @selector(insertParagraphSeparator:) || selector == @selector(insertNewlineIgnoringFieldEditor:))
2284 return "InsertNewline";
2285 if (selector == @selector(insertTabIgnoringFieldEditor:))
2287 if (selector == @selector(pageDown:))
2288 return "MovePageDown";
2289 if (selector == @selector(pageDownAndModifySelection:))
2290 return "MovePageDownAndModifySelection";
2291 if (selector == @selector(pageUp:))
2292 return "MovePageUp";
2293 if (selector == @selector(pageUpAndModifySelection:))
2294 return "MovePageUpAndModifySelection";
2296 // Remove the trailing colon.
2297 const char* selectorName = sel_getName(selector);
2298 size_t selectorNameLength = strlen(selectorName);
2299 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2301 return String(selectorName, selectorNameLength - 1);
2304 - (Editor::Command)coreCommandBySelector:(SEL)selector
2306 Frame* coreFrame = core([self _frame]);
2308 return Editor::Command();
2309 return coreFrame->editor()->command(commandNameForSelector(selector));
2312 - (Editor::Command)coreCommandByName:(const char*)name
2314 Frame* coreFrame = core([self _frame]);
2316 return Editor::Command();
2317 return coreFrame->editor()->command(name);
2320 - (void)executeCoreCommandBySelector:(SEL)selector
2322 if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2324 [self coreCommandBySelector:selector].execute();
2327 - (void)executeCoreCommandByName:(const char*)name
2329 [self coreCommandByName:name].execute();
2332 // These commands are forwarded to the Editor object in WebCore.
2333 // Ideally we'd do this for all editing commands; more of the code
2334 // should be moved from here to there, and more commands should be
2335 // added to this list.
2337 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2338 // The functions are identical.
2340 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2342 WEBCORE_COMMAND(alignCenter)
2343 WEBCORE_COMMAND(alignJustified)
2344 WEBCORE_COMMAND(alignLeft)
2345 WEBCORE_COMMAND(alignRight)
2346 WEBCORE_COMMAND(copy)
2347 WEBCORE_COMMAND(cut)
2348 WEBCORE_COMMAND(delete)
2349 WEBCORE_COMMAND(deleteBackward)
2350 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2351 WEBCORE_COMMAND(deleteForward)
2352 WEBCORE_COMMAND(deleteToBeginningOfLine)
2353 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2354 WEBCORE_COMMAND(deleteToEndOfLine)
2355 WEBCORE_COMMAND(deleteToEndOfParagraph)
2356 WEBCORE_COMMAND(deleteToMark)
2357 WEBCORE_COMMAND(deleteWordBackward)
2358 WEBCORE_COMMAND(deleteWordForward)
2359 WEBCORE_COMMAND(ignoreSpelling)
2360 WEBCORE_COMMAND(indent)
2361 WEBCORE_COMMAND(insertBacktab)
2362 WEBCORE_COMMAND(insertLineBreak)
2363 WEBCORE_COMMAND(insertNewline)
2364 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2365 WEBCORE_COMMAND(insertParagraphSeparator)
2366 WEBCORE_COMMAND(insertTab)
2367 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2368 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
2369 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
2370 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
2371 WEBCORE_COMMAND(moveBackward)
2372 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2373 WEBCORE_COMMAND(moveDown)
2374 WEBCORE_COMMAND(moveDownAndModifySelection)
2375 WEBCORE_COMMAND(moveForward)
2376 WEBCORE_COMMAND(moveForwardAndModifySelection)
2377 WEBCORE_COMMAND(moveLeft)
2378 WEBCORE_COMMAND(moveLeftAndModifySelection)
2379 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2380 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2381 WEBCORE_COMMAND(moveRight)
2382 WEBCORE_COMMAND(moveRightAndModifySelection)
2383 WEBCORE_COMMAND(moveToBeginningOfDocument)
2384 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2385 WEBCORE_COMMAND(moveToBeginningOfLine)
2386 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2387 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2388 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2389 WEBCORE_COMMAND(moveToBeginningOfSentence)
2390 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2391 WEBCORE_COMMAND(moveToEndOfDocument)
2392 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2393 WEBCORE_COMMAND(moveToEndOfLine)
2394 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2395 WEBCORE_COMMAND(moveToEndOfParagraph)
2396 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2397 WEBCORE_COMMAND(moveToEndOfSentence)
2398 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2399 WEBCORE_COMMAND(moveUp)
2400 WEBCORE_COMMAND(moveUpAndModifySelection)
2401 WEBCORE_COMMAND(moveWordBackward)
2402 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2403 WEBCORE_COMMAND(moveWordForward)
2404 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2405 WEBCORE_COMMAND(moveWordLeft)
2406 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2407 WEBCORE_COMMAND(moveWordRight)
2408 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2409 WEBCORE_COMMAND(outdent)
2410 WEBCORE_COMMAND(pageDown)
2411 WEBCORE_COMMAND(pageDownAndModifySelection)
2412 WEBCORE_COMMAND(pageUp)
2413 WEBCORE_COMMAND(pageUpAndModifySelection)
2414 WEBCORE_COMMAND(selectAll)
2415 WEBCORE_COMMAND(selectLine)
2416 WEBCORE_COMMAND(selectParagraph)
2417 WEBCORE_COMMAND(selectSentence)
2418 WEBCORE_COMMAND(selectToMark)
2419 WEBCORE_COMMAND(selectWord)
2420 WEBCORE_COMMAND(setMark)
2421 WEBCORE_COMMAND(subscript)
2422 WEBCORE_COMMAND(superscript)
2423 WEBCORE_COMMAND(swapWithMark)
2424 WEBCORE_COMMAND(transpose)
2425 WEBCORE_COMMAND(underline)
2426 WEBCORE_COMMAND(unscript)
2427 WEBCORE_COMMAND(yank)
2428 WEBCORE_COMMAND(yankAndSelect)
2430 #undef WEBCORE_COMMAND
2432 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2434 - (IBAction)takeFindStringFromSelection:(id)sender
2438 if (![self _hasSelection]) {
2443 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2446 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2448 [pasteboard declareTypes:types owner:[self _topHTMLView]];
2449 [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2453 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2455 Frame* coreFrame = core([self _frame]);
2458 if (coreFrame->selection()->isContentRichlyEditable())
2459 [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
2461 [self _pasteAsPlainTextWithPasteboard:pasteboard];
2465 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2467 BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]);
2468 BOOL isReturnTypeOK = !returnType || ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]);
2469 if (isSendTypeOK && isReturnTypeOK)
2471 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2474 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2475 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2476 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2477 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2478 // might be using the selector, and we don't want to break them.
2479 - (void)jumpToSelection:(id)sender
2483 if (Frame* coreFrame = core([self _frame]))
2484 coreFrame->revealSelection(ScrollAlignment::alignCenterAlways);
2487 - (NSCellStateValue)selectionHasStyle:(CSSStyleDeclaration*)style
2489 Frame* coreFrame = core([self _frame]);
2492 return kit(coreFrame->editor()->selectionHasStyle(style));
2495 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2497 SEL action = [item action];
2498 RefPtr<Frame> frame = core([self _frame]);
2503 if (Document* doc = frame->document()) {
2504 if (doc->isPluginDocument())
2506 if (doc->isImageDocument()) {
2507 if (action == @selector(copy:))
2508 return frame->loader()->isComplete();
2513 if (action == @selector(changeSpelling:)
2514 || action == @selector(_changeSpellingFromMenu:)
2515 || action == @selector(checkSpelling:)
2516 || action == @selector(complete:)
2517 || action == @selector(pasteFont:))
2518 return [self _canEdit];
2520 if (action == @selector(showGuessPanel:)) {
2521 #ifndef BUILDING_ON_TIGER
2522 // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2523 NSMenuItem *menuItem = (NSMenuItem *)item;
2524 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2525 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2526 [menuItem setTitle:panelShowing
2527 ? UI_STRING("Hide Spelling and Grammar", "menu item title")
2528 : UI_STRING("Show Spelling and Grammar", "menu item title")];
2531 return [self _canEdit];
2534 if (action == @selector(changeBaseWritingDirection:)
2535 || action == @selector(makeBaseWritingDirectionLeftToRight:)
2536 || action == @selector(makeBaseWritingDirectionRightToLeft:)) {
2537 NSWritingDirection writingDirection;
2539 if (action == @selector(changeBaseWritingDirection:)) {
2540 writingDirection = static_cast<NSWritingDirection>([item tag]);
2541 if (writingDirection == NSWritingDirectionNatural)
2543 } else if (action == @selector(makeBaseWritingDirectionLeftToRight:))
2544 writingDirection = NSWritingDirectionLeftToRight;
2546 writingDirection = NSWritingDirectionRightToLeft;
2548 NSMenuItem *menuItem = (NSMenuItem *)item;
2549 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2550 RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create();
2552 style->setProperty("direction", writingDirection == NSWritingDirectionLeftToRight ? "LTR" : "RTL", ec);
2553 [menuItem setState:frame->editor()->selectionHasStyle(style.get())];
2555 return [self _canEdit];
2558 if (action == @selector(makeBaseWritingDirectionNatural:)) {
2559 NSMenuItem *menuItem = (NSMenuItem *)item;
2560 if ([menuItem isKindOfClass:[NSMenuItem class]])
2561 [menuItem setState:NSOffState];
2565 if (action == @selector(toggleBaseWritingDirection:)) {
2566 NSMenuItem *menuItem = (NSMenuItem *)item;
2567 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2568 RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create();
2570 style->setProperty("direction", "RTL", ec);
2571 // Take control of the title of the menu item instead of just checking/unchecking it because
2572 // a check would be ambiguous.
2573 [menuItem setTitle:frame->editor()->selectionHasStyle(style.get())
2574 ? UI_STRING("Left to Right", "Left to Right context menu item")
2575 : UI_STRING("Right to Left", "Right to Left context menu item")];
2577 return [self _canEdit];
2580 if (action == @selector(changeAttributes:)
2581 || action == @selector(changeColor:)
2582 || action == @selector(changeFont:))
2583 return [self _canEditRichly];
2585 if (action == @selector(capitalizeWord:)
2586 || action == @selector(lowercaseWord:)
2587 || action == @selector(uppercaseWord:))
2588 return [self _hasSelection] && [self _isEditable];
2590 if (action == @selector(centerSelectionInVisibleArea:)
2591 || action == @selector(jumpToSelection:)
2592 || action == @selector(copyFont:))
2593 return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2595 if (action == @selector(changeDocumentBackgroundColor:))
2596 return [[self _webView] isEditable] && [self _canEditRichly];
2598 if (action == @selector(_ignoreSpellingFromMenu:)
2599 || action == @selector(_learnSpellingFromMenu:)
2600 || action == @selector(takeFindStringFromSelection:))
2601 return [self _hasSelection];
2603 if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2604 return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste());
2606 if (action == @selector(pasteAsRichText:))
2607 return frame && (frame->editor()->canDHTMLPaste()
2608 || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable()));
2610 if (action == @selector(performFindPanelAction:))
2613 if (action == @selector(_lookUpInDictionaryFromMenu:))
2614 return [self _hasSelection];
2616 #ifndef BUILDING_ON_TIGER
2617 if (action == @selector(toggleGrammarChecking:)) {
2618 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate
2619 // the selector here because we implement it here, and we must implement it here because the AppKit
2620 // code checks the first responder.
2621 NSMenuItem *menuItem = (NSMenuItem *)item;
2622 if ([menuItem isKindOfClass:[NSMenuItem class]])
2623 [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2628 Editor::Command command = [self coreCommandBySelector:action];
2629 if (command.isSupported()) {
2630 NSMenuItem *menuItem = (NSMenuItem *)item;
2631 if ([menuItem isKindOfClass:[NSMenuItem class]])
2632 [menuItem setState:kit(command.state())];
2633 return command.isEnabled();
2639 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2641 // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
2642 // assumes the WebVIew is non-nil.
2643 if (![self _webView])
2645 BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2646 return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2649 - (BOOL)acceptsFirstResponder
2651 // Don't accept first responder when we first click on this view.
2652 // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2653 // Do accept first responder at any other time, for example from keyboard events,
2654 // or from calls back from WebCore once we begin mouse-down event handling.
2655 NSEvent *event = [NSApp currentEvent];
2656 if ([event type] == NSLeftMouseDown
2657 && !_private->handlingMouseDownEvent
2658 && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2664 - (BOOL)maintainsInactiveSelection
2666 // This method helps to determine whether the WebHTMLView should maintain
2667 // an inactive selection when it's not first responder.
2668 // Traditionally, these views have not maintained such selections,
2669 // clearing them when the view was not first responder. However,
2670 // to fix bugs like this one:
2671 // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even
2672 // when they're not firstResponder"
2673 // it was decided to add a switch to act more like an NSTextView.
2675 if ([[self _webView] maintainsInactiveSelection])
2678 // Predict the case where we are losing first responder status only to
2679 // gain it back again. Want to keep the selection in that case.
2680 id nextResponder = [[self window] _newFirstResponderAfterResigning];
2681 if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2682 id contentView = [nextResponder contentView];
2684 nextResponder = contentView;
2686 if ([nextResponder isKindOfClass:[NSClipView class]]) {
2687 id documentView = [nextResponder documentView];
2689 nextResponder = documentView;
2691 if (nextResponder == self)
2694 Frame* coreFrame = core([self _frame]);
2695 bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable();
2696 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2697 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2699 return selectionIsEditable && nextResponderIsInWebView;
2702 - (void)addMouseMovedObserver
2704 if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications)
2707 // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2708 if (!([[self window] isKeyWindow]
2709 #if ENABLE(DASHBOARD_SUPPORT)
2710 || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]
2715 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2716 name:WKMouseMovedNotification() object:nil];
2717 [self _frameOrBoundsChanged];
2718 _private->observingMouseMovedNotifications = true;
2721 - (void)removeMouseMovedObserver
2723 #if ENABLE(DASHBOARD_SUPPORT)
2724 // Don't remove the observer if we're running the Dashboard.
2725 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2729 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2730 [self _removeMouseMovedObserverUnconditionally];
2733 - (void)addSuperviewObservers
2735 // We watch the bounds of our superview, so that we can do a layout when the size
2736 // of the superview changes. This is different from other scrollable things that don't
2737 // need this kind of thing because their layout doesn't change.
2739 // We need to pay attention to both height and width because our "layout" has to change
2740 // to extend the background the full height of the space and because some elements have
2741 // sizes that are based on the total size of the view.
2743 if (_private->observingSuperviewNotifications)
2746 NSView *superview = [self superview];
2747 if (!superview || ![self window])
2750 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2751 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview];
2752 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview];
2754 // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2755 // It will check the current size/scroll against the previous layout's size/scroll. We need to
2756 // do this here to catch the case where the WebView is laid out at one size, removed from its
2757 // window, resized, and inserted into another window. Our frame/bounds changed notifications
2758 // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2759 [self _frameOrBoundsChanged];
2761 _private->observingSuperviewNotifications = true;
2764 - (void)addWindowObservers
2766 if (_private->observingWindowNotifications)
2769 NSWindow *window = [self window];
2773 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2774 [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
2775 [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
2776 [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
2777 [notificationCenter addObserver:self selector:@selector(windowWillOrderOnScreen:) name:WKWindowWillOrderOnScreenNotification() object:window];
2779 _private->observingWindowNotifications = true;
2782 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2784 [self _removeSuperviewObservers];
2787 - (void)viewDidMoveToSuperview
2789 if ([self superview] != nil)
2790 [self addSuperviewObservers];
2793 static void _updateFocusedAndActiveStateTimerCallback(CFRunLoopTimerRef timer, void *info)
2795 WebHTMLView *view = (WebHTMLView *)info;
2796 [view _updateFocusedAndActiveState];
2799 - (void)viewWillMoveToWindow:(NSWindow *)window
2801 // Don't do anything if we aren't initialized. This happens
2802 // when decoding a WebView. When WebViews are decoded their subviews
2803 // are created by initWithCoder: and so won't be normally
2804 // initialized. The stub views are discarded by WebView.
2808 // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2809 [self _removeMouseMovedObserverUnconditionally];
2810 [self _removeWindowObservers];
2811 [self _removeSuperviewObservers];
2812 [self _cancelUpdateMouseoverTimer];
2813 [self _cancelUpdateFocusedAndActiveStateTimer];
2815 [[self _pluginController] stopAllPlugins];
2818 - (void)viewDidMoveToWindow
2820 // Don't do anything if we aren't initialized. This happens
2821 // when decoding a WebView. When WebViews are decoded their subviews
2822 // are created by initWithCoder: and so won't be normally
2823 // initialized. The stub views are discarded by WebView.
2824 if (!_private || _private->closed)
2827 [self _stopAutoscrollTimer];
2828 if ([self window]) {
2829 _private->lastScrollPosition = [[self superview] bounds].origin;
2830 [self addWindowObservers];
2831 [self addSuperviewObservers];
2832 [self addMouseMovedObserver];
2834 // Schedule this update, rather than making the call right now.
2835 // The reason is that placing the caret in the just-installed view requires
2836 // the HTML/XML document to be available on the WebCore side, but it is not
2837 // at the time this code is running. However, it will be there on the next
2838 // crank of the run loop. Doing this helps to make a blinking caret appear
2839 // in a new, empty window "automatic".
2840 if (!_private->updateFocusedAndActiveStateTimer) {
2841 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
2842 _private->updateFocusedAndActiveStateTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0,
2843 _updateFocusedAndActiveStateTimerCallback, &context);
2844 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateFocusedAndActiveStateTimer, kCFRunLoopDefaultMode);
2847 [[self _pluginController] startAllPlugins];
2849 _private->lastScrollPosition = NSZeroPoint;
2853 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2855 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2858 - (void)viewDidMoveToHostWindow
2860 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2864 - (void)addSubview:(NSView *)view
2866 [super addSubview:view];
2868 if ([WebPluginController isPlugInView:view])
2869 [[self _pluginController] addPlugin:view];
2872 - (void)willRemoveSubview:(NSView *)subview
2874 if ([WebPluginController isPlugInView:subview])
2875 [[self _pluginController] destroyPlugin:subview];
2877 [super willRemoveSubview:subview];
2880 - (void)reapplyStyles
2882 if (!_private->needsToApplyStyles)
2886 double start = CFAbsoluteTimeGetCurrent();
2889 if (Frame* coreFrame = core([self _frame])) {
2890 if (FrameView* coreView = coreFrame->view())
2891 coreView->setMediaType(_private->printing ? "print" : "screen");
2892 if (Document* document = coreFrame->document())
2893 document->setPrinting(_private->printing);
2894 coreFrame->reapplyStyles();
2898 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2899 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2902 _private->needsToApplyStyles = NO;
2905 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2906 // minPageWidth==0 implies a non-printing layout
2907 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
2909 [self reapplyStyles];
2911 if (!_private->needsLayout && ![[self _frame] _needsLayout])
2915 double start = CFAbsoluteTimeGetCurrent();
2918 LOG(View, "%@ doing layout", self);
2920 Frame* coreFrame = core([self _frame]);
2922 _private->needsLayout = NO;
2926 if (FrameView* coreView = coreFrame->view()) {
2927 if (minPageWidth > 0.0)
2928 coreView->forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize);
2930 coreView->forceLayout(!adjustViewSize);
2932 coreView->adjustViewSize();
2935 _private->needsLayout = NO;
2937 if (!_private->printing)
2938 _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
2941 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2942 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
2948 [self layoutToMinimumPageWidth:0.0f maximumPageWidth:0.0f adjustingViewSize:NO];
2951 // Deliver mouseup events to the DOM for button 2.
2952 - (void)rightMouseUp:(NSEvent *)event
2954 [super rightMouseUp:event];
2955 if (Frame* coreframe = core([self _frame]))
2956 coreframe->eventHandler()->mouseUp(event);
2959 - (NSMenu *)menuForEvent:(NSEvent *)event
2961 [_private->compController endRevertingChange:NO moveLeft:NO];
2963 _private->handlingMouseDownEvent = YES;
2964 BOOL handledEvent = NO;
2965 Frame* coreFrame = core([self _frame]);
2968 _private->handlingMouseDownEvent = NO;
2972 Page* page = coreFrame->page();
2976 page->contextMenuController()->clearContextMenu();
2977 // Match behavior of other browsers by sending an onmousedown event for right clicks.
2978 coreFrame->eventHandler()->mouseDown(event);
2979 handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformMouseEvent(event));
2980 _private->handlingMouseDownEvent = NO;
2985 ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
2989 NSArray* menuItems = coreMenu->platformDescription();
2991 if (menuItems && [menuItems count] > 0) {
2992 menu = [[[NSMenu alloc] init] autorelease];
2993 for (unsigned i = 0; i < [menuItems count]; i++)
2994 [menu addItem:[menuItems objectAtIndex:i]];
3000 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
3002 return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
3007 Frame* coreFrame = core([self _frame]);
3010 Document* document = coreFrame->document();
3014 document->setFocusedNode(0);
3019 return [[self _webView] drawsBackground];
3022 - (void)setNeedsDisplay:(BOOL)flag
3024 LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO");
3025 [super setNeedsDisplay:flag];
3028 - (void)setNeedsLayout: (BOOL)flag
3030 LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO");
3031 _private->needsLayout = flag;
3034 - (void)setNeedsToApplyStyles: (BOOL)flag
3036 LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO");
3037 _private->needsToApplyStyles = flag;
3040 - (void)drawSingleRect:(NSRect)rect
3042 [NSGraphicsContext saveGraphicsState];
3045 ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
3047 [(WebClipView *)[self superview] setAdditionalClip:rect];
3050 if ([self _transparentBackground]) {
3051 [[NSColor clearColor] set];
3055 [[self _frame] _drawRect:rect contentsOnly:YES];
3057 WebView *webView = [self _webView];
3059 // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
3060 // called after the WebView has closed. If the client did not properly close the WebView and set the
3061 // UIDelegate to nil, then the UIDelegate will be stale and this code will crash.
3062 static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
3063 if (version3OrLaterClient)
3064 [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
3066 if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight])
3067 [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]];
3069 [(WebClipView *)[self superview] resetAdditionalClip];
3071 [NSGraphicsContext restoreGraphicsState];
3072 } @catch (NSException *localException) {
3073 [(WebClipView *)[self superview] resetAdditionalClip];
3074 [NSGraphicsContext restoreGraphicsState];
3075 LOG_ERROR("Exception caught while drawing: %@", localException);
3076 [localException raise];
3080 - (void)drawRect:(NSRect)rect
3082 ASSERT_MAIN_THREAD();
3083 LOG(View, "%@ drawing", self);
3085 const NSRect *rects;
3087 [self getRectsBeingDrawn:&rects count:&count];
3089 BOOL subviewsWereSetAside = _private->subviewsSetAside;
3090 if (subviewsWereSetAside)
3091 [self _restoreSubviews];
3094 double start = CFAbsoluteTimeGetCurrent();
3097 if ([[self _webView] _mustDrawUnionedRect:rect singleRects:rects count:count])
3098 [self drawSingleRect:rect];
3100 for (int i = 0; i < count; ++i)
3101 [self drawSingleRect:rects[i]];
3104 double thisTime = CFAbsoluteTimeGetCurrent() - start;
3105 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
3108 if (subviewsWereSetAside)
3109 [self _setAsideSubviews];
3111 #if USE(ACCELERATED_COMPOSITING)
3112 if ([[self _webView] _needsOneShotDrawingSynchronization]) {
3113 // Disable screen updates so that drawing into the NSView and
3114 // CALayer updates appear on the screen at the same time.
3115 [[self window] disableScreenUpdatesUntilFlush];
3116 [CATransaction flush];
3117 [[self _webView] _setNeedsOneShotDrawingSynchronization:NO];
3122 // Turn off the additional clip while computing our visibleRect.
3123 - (NSRect)visibleRect
3125 if (!([[self superview] isKindOfClass:[WebClipView class]]))
3126 return [super visibleRect];
3128 WebClipView *clipView = (WebClipView *)[self superview];
3130 BOOL hasAdditionalClip = [clipView hasAdditionalClip];
3131 if (!hasAdditionalClip) {
3132 return [super visibleRect];
3135 NSRect additionalClip = [clipView additionalClip];
3136 [clipView resetAdditionalClip];
3137 NSRect visibleRect = [super visibleRect];
3138 [clipView setAdditionalClip:additionalClip];
3147 - (void)windowDidBecomeKey:(NSNotification *)notification
3149 NSWindow *keyWindow = [notification object];
3151 if (keyWindow == [self window])
3152 [self addMouseMovedObserver];
3154 if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
3155 [self _updateFocusedAndActiveState];
3158 - (void)windowDidResignKey:(NSNotification *)notification
3160 NSWindow *formerKeyWindow = [notification object];
3162 if (formerKeyWindow == [self window])
3163 [self removeMouseMovedObserver];
3165 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
3166 [self _updateFocusedAndActiveState];
3167 [_private->compController endRevertingChange:NO moveLeft:NO];
3171 - (void)windowWillClose:(NSNotification *)notification
3173 [_private->compController endRevertingChange:NO moveLeft:NO];
3174 [[self _pluginController] destroyAllPlugins];
3177 - (void)windowWillOrderOnScreen:(NSNotification *)notification
3179 if (![[self _webView] shouldUpdateWhileOffscreen])
3180 [self setNeedsDisplay:YES];
3183 - (void)scrollWheel:(NSEvent *)event
3186 Frame* frame = core([self _frame]);
3187 if (!frame || !frame->eventHandler()->wheelEvent(event))
3188 [super scrollWheel:event];
3192 - (BOOL)_isSelectionEvent:(NSEvent *)event
3194 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3195 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
3198 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3200 NSView *hitView = [self _hitViewForEvent:event];
3201 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3203 #if ENABLE(DASHBOARD_SUPPORT)
3204 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
3209 bool result = false;
3210 if (Frame* coreFrame = core([hitHTMLView _frame])) {
3211 coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
3212 [hitHTMLView _setMouseDownEvent:event];
3213 if ([hitHTMLView _isSelectionEvent:event])
3214 result = coreFrame->eventHandler()->eventMayStartDrag(event);
3215 [hitHTMLView _setMouseDownEvent:nil];
3219 return [hitView acceptsFirstMouse:event];
3222 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3224 NSView *hitView = [self _hitViewForEvent:event];
3225 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3227 bool result = false;
3228 if ([hitHTMLView _isSelectionEvent:event])
3229 if (Frame* coreFrame = core([hitHTMLView _frame])) {
3230 [hitHTMLView _setMouseDownEvent:event];
3231 result = coreFrame->eventHandler()->eventMayStartDrag(event);
3232 [hitHTMLView _setMouseDownEvent:nil];
3236 return [hitView shouldDelayWindowOrderingForEvent:event];
3239 - (void)mouseDown:(NSEvent *)event
3241 RetainPtr<WebHTMLView> protector = self;
3242 if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
3245 _private->handlingMouseDownEvent = YES;
3247 // Record the mouse down position so we can determine drag hysteresis.
3248 [self _setMouseDownEvent:event];
3250 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3251 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3254 [_private->compController endRevertingChange:NO moveLeft:NO];
3256 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
3257 // We don't want to pass them along to KHTML a second time.
3258 if (!([event modifierFlags] & NSControlKeyMask)) {
3259 _private->ignoringMouseDraggedEvents = NO;
3261 // Don't do any mouseover while the mouse is down.
3262 [self _cancelUpdateMouseoverTimer];
3264 // Let WebCore get a chance to deal with the event. This will call back to us
3265 // to start the autoscroll timer if appropriate.
3266 if (Frame* coreframe = core([self _frame]))
3267 coreframe->eventHandler()->mouseDown(event);
3271 _private->handlingMouseDownEvent = NO;
3274 - (void)dragImage:(NSImage *)dragImage
3276 offset:(NSSize)offset
3277 event:(NSEvent *)event
3278 pasteboard:(NSPasteboard *)pasteboard
3280 slideBack:(BOOL)slideBack
3282 ASSERT(self == [self _topHTMLView]);
3283 [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
3286 - (void)mouseDragged:(NSEvent *)event
3288 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3289 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3294 if (!_private->ignoringMouseDraggedEvents)
3295 if (Frame* coreframe = core([self _frame]))
3296 coreframe->eventHandler()->mouseDragged(event);
3301 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
3303 ASSERT(![self _webView] || [self _isTopHTMLView]);
3305 Page *page = core([self _webView]);
3308 return NSDragOperationNone;
3310 if (page->dragController()->dragOperation() == DragOperationNone)
3311 return NSDragOperationGeneric | NSDragOperationCopy;
3313 return (NSDragOperation)page->dragController()->dragOperation();
3316 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
3318 ASSERT(![self _webView] || [self _isTopHTMLView]);
3320 NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
3321 NSPoint windowMouseLoc = windowImageLoc;
3323 if (Page* page = core([self _webView])) {
3324 DragController* dragController = page->dragController();
3325 NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3328 [[self _frame] _dragSourceMovedTo:windowMouseLoc];
3331 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
3333 ASSERT(![self _webView] || [self _isTopHTMLView]);
3335 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
3336 NSPoint windowMouseLoc = windowImageLoc;
3338 if (Page* page = core([self _webView])) {
3339 DragController* dragController = page->dragController();
3340 windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3341 dragController->dragEnded();
3344 [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation];
3346 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3347 _private->ignoringMouseDraggedEvents = YES;
3349 // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
3350 // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
3351 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
3352 location:windowMouseLoc
3353 modifierFlags:[[NSApp currentEvent] modifierFlags]
3354 timestamp:[NSDate timeIntervalSinceReferenceDate]
3355 windowNumber:[[self window] windowNumber]
3356 context:[[NSApp currentEvent] context]
3357 eventNumber:0 clickCount:0 pressure:0];
3358 [self mouseUp:fakeEvent]; // This will also update the mouseover state.
3361 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3363 NSFileWrapper *wrapper = nil;
3364 NSURL *draggingImageURL = nil;
3366 if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) {
3368 SharedBuffer *buffer = static_cast<CachedResource*>(tiffResource)->data();
3370 goto noPromisedData;
3372 NSData *data = buffer->createNSData();
3373 NSURLResponse *response = tiffResource->response().nsURLResponse();
3374 draggingImageURL = [response URL];
3375 wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
3376 NSString* filename = [response suggestedFilename];
3377 NSString* trueExtension(tiffResource->image()->filenameExtension());
3378 if (![filename matchesExtensionEquivalent:trueExtension])
3379 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension];
3380 [wrapper setPreferredFilename:filename];
3386 ASSERT(![self _webView] || [self _isTopHTMLView]);
3387 Page* page = core([self _webView]);
3389 //If a load occurs midway through a drag, the view may be detached, which gives
3390 //us no ability to get to the original Page, so we cannot access any drag state
3391 //FIXME: is there a way to recover?
3395 const KURL& imageURL = page->dragController()->draggingImageURL();
3396 ASSERT(!imageURL.isEmpty());
3397 draggingImageURL = imageURL;
3399 wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL];
3402 if (wrapper == nil) {
3403 LOG_ERROR("Failed to create image file.");
3407 // FIXME: Report an error if we fail to create a file.
3408 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3409 path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
3410 if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
3411 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
3413 if (draggingImageURL)
3414 [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path];
3416 return [NSArray arrayWithObject:[path lastPathComponent]];
3419 - (void)mouseUp:(NSEvent *)event
3421 [self _setMouseDownEvent:nil];
3423 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3424 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3429 [self _stopAutoscrollTimer];
3430 if (Frame* coreframe = core([self _frame]))
3431 coreframe->eventHandler()->mouseUp(event);
3432 [self _updateMouseoverWithFakeEvent];
3437 - (void)mouseMovedNotification:(NSNotification *)notification
3439 [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
3442 // returning YES from this method is the way we tell AppKit that it is ok for this view
3443 // to be in the key loop even when "tab to all controls" is not on.
3444 - (BOOL)needsPanelToBecomeKey
3449 - (BOOL)becomeFirstResponder
3451 NSSelectionDirection direction = NSDirectSelection;
3452 if (![[self _webView] _isPerformingProgrammaticFocus])
3453 direction = [[self window] keyViewSelectionDirection];
3455 [self _updateFocusedAndActiveState];
3456 [self _updateFontPanel];
3458 Frame* frame = core([self _frame]);
3462 frame->editor()->setStartNewKillRingSequence(true);
3464 if (direction == NSDirectSelection)
3467 Page* page = frame->page();
3471 page->focusController()->setFocusedFrame(frame);
3472 if (Document* document = frame->document())
3473 document->setFocusedNode(0);
3474 page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward,
3475 frame->eventHandler()->currentKeyboardEvent().get());
3479 - (BOOL)resignFirstResponder
3481 BOOL resign = [super resignFirstResponder];
3483 _private->resigningFirstResponder = YES;
3484 [_private->compController endRevertingChange:NO moveLeft:NO];
3485 if (![self maintainsInactiveSelection]) {
3487 if (![[self _webView] _isPerformingProgrammaticFocus])
3490 [self _updateFocusedAndActiveState];
3491 _private->resigningFirstResponder = NO;
3496 - (void)setDataSource:(WebDataSource *)dataSource
3499 if (_private->dataSource != dataSource) {
3500 ASSERT(!_private->closed);
3501 BOOL hadDataSource = _private->dataSource != nil;
3503 [dataSource retain];
3504 [_private->dataSource release];
3505 _private->dataSource = dataSource;
3506 [_private->pluginController setDataSource:dataSource];
3509 [self addMouseMovedObserver];
3513 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3517 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3518 // key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3519 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3520 - (void)updateCell:(NSCell*)cell
3524 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3525 // pageWidth != 0 implies we will relayout to a new width
3526 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
3528 WebFrame *frame = [self _frame];
3529 NSArray *subframes = [frame childFrames];
3530 unsigned n = [subframes count];
3532 for (i = 0; i != n; ++i) {
3533 WebFrame *subframe = [subframes objectAtIndex:i];
3534 WebFrameView *frameView = [subframe frameView];
3535 if ([[subframe _dataSource] _isDocumentHTML]) {