2 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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 "DOMCSSStyleDeclarationInternal.h"
33 #import "DOMDocumentFragmentInternal.h"
34 #import "DOMDocumentInternal.h"
35 #import "DOMNodeInternal.h"
36 #import "DOMRangeInternal.h"
37 #import "WebArchive.h"
38 #import "WebClipView.h"
39 #import "WebDOMOperationsInternal.h"
40 #import "WebDataSourceInternal.h"
41 #import "WebDefaultUIDelegate.h"
42 #import "WebDelegateImplementationCaching.h"
43 #import "WebDocumentInternal.h"
44 #import "WebDynamicScrollBarsViewInternal.h"
45 #import "WebEditingDelegate.h"
46 #import "WebElementDictionary.h"
47 #import "WebFrameInternal.h"
48 #import "WebFramePrivate.h"
49 #import "WebFrameViewInternal.h"
50 #import "WebHTMLRepresentationPrivate.h"
51 #import "WebHTMLViewInternal.h"
52 #import "WebKitLogging.h"
53 #import "WebKitNSStringExtras.h"
54 #import "WebKitVersionChecks.h"
55 #import "WebLocalizableStringsInternal.h"
56 #import "WebNSEventExtras.h"
57 #import "WebNSFileManagerExtras.h"
58 #import "WebNSImageExtras.h"
59 #import "WebNSObjectExtras.h"
60 #import "WebNSPasteboardExtras.h"
61 #import "WebNSPrintOperationExtras.h"
62 #import "WebNSURLExtras.h"
63 #import "WebNSViewExtras.h"
64 #import "WebNetscapePluginView.h"
65 #import "WebNodeHighlight.h"
66 #import "WebPluginController.h"
67 #import "WebPreferences.h"
68 #import "WebPreferencesPrivate.h"
69 #import "WebResourcePrivate.h"
70 #import "WebTextCompletionController.h"
71 #import "WebTypesInternal.h"
72 #import "WebUIDelegatePrivate.h"
73 #import "WebViewInternal.h"
74 #import <AppKit/NSAccessibility.h>
75 #import <ApplicationServices/ApplicationServices.h>
76 #import <WebCore/CSSMutableStyleDeclaration.h>
77 #import <WebCore/CachedImage.h>
78 #import <WebCore/CachedResourceClient.h>
79 #import <WebCore/CachedResourceLoader.h>
80 #import <WebCore/Chrome.h>
81 #import <WebCore/ColorMac.h>
82 #import <WebCore/ContextMenu.h>
83 #import <WebCore/ContextMenuController.h>
84 #import <WebCore/Document.h>
85 #import <WebCore/DocumentFragment.h>
86 #import <WebCore/DocumentMarkerController.h>
87 #import <WebCore/DragController.h>
88 #import <WebCore/Editor.h>
89 #import <WebCore/EditorDeleteAction.h>
90 #import <WebCore/Element.h>
91 #import <WebCore/EventHandler.h>
92 #import <WebCore/ExceptionHandlers.h>
93 #import <WebCore/FloatRect.h>
94 #import <WebCore/FocusController.h>
95 #import <WebCore/Frame.h>
96 #import <WebCore/FrameLoader.h>
97 #import <WebCore/FrameSelection.h>
98 #import <WebCore/FrameView.h>
99 #import <WebCore/HTMLConverter.h>
100 #import <WebCore/HTMLNames.h>
101 #import <WebCore/HitTestResult.h>
102 #import <WebCore/Image.h>
103 #import <WebCore/KeyboardEvent.h>
104 #import <WebCore/LegacyWebArchive.h>
105 #import <WebCore/MIMETypeRegistry.h>
106 #import <WebCore/Page.h>
107 #import <WebCore/PlatformKeyboardEvent.h>
108 #import <WebCore/Range.h>
109 #import <WebCore/RenderWidget.h>
110 #import <WebCore/RenderView.h>
111 #import <WebCore/RuntimeApplicationChecks.h>
112 #import <WebCore/SharedBuffer.h>
113 #import <WebCore/SimpleFontData.h>
114 #import <WebCore/Text.h>
115 #import <WebCore/WebCoreObjCExtras.h>
116 #import <WebCore/WebFontCache.h>
117 #import <WebCore/WebNSAttributedStringExtras.h>
118 #import <WebCore/markup.h>
119 #import <WebKit/DOM.h>
120 #import <WebKit/DOMExtensions.h>
121 #import <WebKit/DOMPrivate.h>
122 #import <WebKitSystemInterface.h>
125 #import <runtime/InitializeThreading.h>
126 #import <wtf/MainThread.h>
128 #if USE(ACCELERATED_COMPOSITING)
129 #import <QuartzCore/QuartzCore.h>
132 using namespace WebCore;
133 using namespace HTMLNames;
137 @interface WebMenuTarget : NSObject {
138 WebCore::ContextMenuController* _menuController;
140 + (WebMenuTarget*)sharedMenuTarget;
141 - (WebCore::ContextMenuController*)menuController;
142 - (void)setMenuController:(WebCore::ContextMenuController*)menuController;
143 - (void)forwardContextMenuAction:(id)sender;
144 - (BOOL)validateMenuItem:(NSMenuItem *)item;
147 static WebMenuTarget* target;
149 @implementation WebMenuTarget
151 + (WebMenuTarget*)sharedMenuTarget
154 target = [[WebMenuTarget alloc] init];
158 - (WebCore::ContextMenuController*)menuController
160 return _menuController;
163 - (void)setMenuController:(WebCore::ContextMenuController*)menuController
165 _menuController = menuController;
168 - (void)forwardContextMenuAction:(id)sender
170 WebCore::ContextMenuItem item(WebCore::ActionType, static_cast<WebCore::ContextMenuAction>([sender tag]), [sender title]);
171 _menuController->contextMenuItemSelected(&item);
174 - (BOOL)validateMenuItem:(NSMenuItem *)item
176 WebCore::ContextMenuItem coreItem(item);
177 ASSERT(_menuController->contextMenu());
178 _menuController->checkOrEnableIfNeeded(coreItem);
179 return coreItem.enabled();
184 @interface NSWindow (BorderViewAccess)
185 - (NSView*)_web_borderView;
188 @implementation NSWindow (BorderViewAccess)
189 - (NSView*)_web_borderView
195 @interface WebResponderChainSink : NSResponder {
196 NSResponder* _lastResponderInChain;
197 BOOL _receivedUnhandledCommand;
199 - (id)initWithResponderChain:(NSResponder *)chain;
201 - (BOOL)receivedUnhandledCommand;
204 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
205 static BOOL forceNSViewHitTest;
207 // 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])
208 static BOOL forceWebHTMLViewHitTest;
210 static WebHTMLView *lastHitView;
212 static bool needsCursorRectsSupportAtPoint(NSWindow* window, NSPoint point)
214 forceNSViewHitTest = YES;
215 NSView* view = [[window _web_borderView] hitTest:point];
216 forceNSViewHitTest = NO;
218 // WebHTMLView doesn't use cursor rects.
219 if ([view isKindOfClass:[WebHTMLView class]])
222 #if ENABLE(NETSCAPE_PLUGIN_API)
223 // Neither do NPAPI plug-ins.
224 if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
228 // Non-Web content, WebPDFView, and WebKit plug-ins use normal cursor handling.
233 static IMP oldSetCursorForMouseLocationIMP;
235 // Overriding an internal method is a hack; <rdar://problem/7662987> tracks finding a better solution.
236 static void setCursor(NSWindow *self, SEL cmd, NSPoint point)
238 if (needsCursorRectsSupportAtPoint(self, point))
239 oldSetCursorForMouseLocationIMP(self, cmd, point);
245 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
247 extern NSString *NSMarkedClauseSegmentAttributeName;
248 extern NSString *NSTextInputReplacementRangeAttributeName;
252 @interface NSView (WebNSViewDetails)
253 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
254 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
255 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView;
256 - (NSRect)_dirtyRect;
257 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
258 - (BOOL)_drawnByAncestor;
259 - (void)_invalidateGStatesForTree;
260 - (void)_propagateDirtyRectsToOpaqueAncestors;
261 - (void)_windowChangedKeyState;
262 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
263 - (void)_updateLayerGeometryFromView;
267 #if USE(ACCELERATED_COMPOSITING)
268 static IMP oldSetNeedsDisplayInRectIMP;
270 static void setNeedsDisplayInRect(NSView *self, SEL cmd, NSRect invalidRect)
272 if (![self _drawnByAncestor]) {
273 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
277 static Class webFrameViewClass = [WebFrameView class];
278 WebFrameView *enclosingWebFrameView = (WebFrameView *)self;
279 while (enclosingWebFrameView && ![enclosingWebFrameView isKindOfClass:webFrameViewClass])
280 enclosingWebFrameView = (WebFrameView *)[enclosingWebFrameView superview];
282 if (!enclosingWebFrameView) {
283 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
287 Frame* coreFrame = core([enclosingWebFrameView webFrame]);
288 FrameView* frameView = coreFrame ? coreFrame->view() : 0;
289 if (!frameView || !frameView->isEnclosedInCompositingLayer()) {
290 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
294 NSRect invalidRectInWebFrameViewCoordinates = [enclosingWebFrameView convertRect:invalidRect fromView:self];
295 IntRect invalidRectInFrameViewCoordinates(invalidRectInWebFrameViewCoordinates);
296 if (![enclosingWebFrameView isFlipped])
297 invalidRectInFrameViewCoordinates.setY(frameView->frameRect().size().height() - invalidRectInFrameViewCoordinates.maxY());
299 frameView->invalidateRect(invalidRectInFrameViewCoordinates);
301 #endif // USE(ACCELERATED_COMPOSITING)
303 @interface NSApplication (WebNSApplicationDetails)
304 - (void)speakString:(NSString *)string;
307 @interface NSWindow (WebNSWindowDetails)
308 - (id)_newFirstResponderAfterResigning;
311 @interface NSAttributedString (WebNSAttributedStringDetails)
312 - (id)_initWithDOMRange:(DOMRange *)range;
313 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
316 @interface NSSpellChecker (WebNSSpellCheckerDetails)
317 - (void)learnWord:(NSString *)word;
320 // By imaging to a width a little wider than the available pixels,
321 // thin pages will be scaled down a little, matching the way they
322 // print in IE and Camino. This lets them use fewer sheets than they
323 // would otherwise, which is presumably why other browsers do this.
324 // Wide pages will be scaled down more than this.
325 const float _WebHTMLViewPrintingMinimumShrinkFactor = 1.25;
327 // This number determines how small we are willing to reduce the page content
328 // in order to accommodate the widest line. If the page would have to be
329 // reduced smaller to make the widest line fit, we just clip instead (this
330 // behavior matches MacIE and Mozilla, at least)
331 const float _WebHTMLViewPrintingMaximumShrinkFactor = 2;
333 #define AUTOSCROLL_INTERVAL 0.1f
335 // Any non-zero value will do, but using something recognizable might help us debug some day.
336 #define TRACKING_RECT_TAG 0xBADFACE
338 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
339 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
341 #define STANDARD_WEIGHT 5
342 #define MIN_BOLD_WEIGHT 7
343 #define STANDARD_BOLD_WEIGHT 9
346 #define WebDataProtocolScheme @"webkit-fake-url"
348 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
349 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
350 @interface WebCoreScrollView : NSScrollView
353 @implementation WebCoreScrollView
356 // We need this to be able to safely reference the CachedImage for the promised drag data
357 static CachedResourceClient* promisedDataClient()
359 static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
360 return staticCachedResourceClient;
363 @interface WebHTMLView (WebHTMLViewFileInternal)
364 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
365 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
366 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
367 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
368 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
369 - (void)_removeMouseMovedObserverUnconditionally;
370 - (void)_removeSuperviewObservers;
371 - (void)_removeWindowObservers;
372 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
373 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
374 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
375 - (DOMRange *)_selectedRange;
376 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
377 - (NSView *)_hitViewForEvent:(NSEvent *)event;
378 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
379 - (DOMRange *)_documentRange;
380 - (void)_setMouseDownEvent:(NSEvent *)event;
381 - (WebHTMLView *)_topHTMLView;
382 - (BOOL)_isTopHTMLView;
383 - (void)_web_setPrintingModeRecursive;
384 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
385 - (void)_web_clearPrintingModeRecursive;
388 #ifndef BUILDING_ON_LEOPARD
390 @interface WebHTMLView (WebHTMLViewTextCheckingInternal)
391 - (void)orderFrontSubstitutionsPanel:(id)sender;
392 - (BOOL)smartInsertDeleteEnabled;
393 - (void)setSmartInsertDeleteEnabled:(BOOL)flag;
394 - (void)toggleSmartInsertDelete:(id)sender;
395 - (BOOL)isAutomaticQuoteSubstitutionEnabled;
396 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag;
397 - (void)toggleAutomaticQuoteSubstitution:(id)sender;
398 - (BOOL)isAutomaticLinkDetectionEnabled;
399 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag;
400 - (void)toggleAutomaticLinkDetection:(id)sender;
401 - (BOOL)isAutomaticDashSubstitutionEnabled;
402 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag;
403 - (void)toggleAutomaticDashSubstitution:(id)sender;
404 - (BOOL)isAutomaticTextReplacementEnabled;
405 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag;
406 - (void)toggleAutomaticTextReplacement:(id)sender;
407 - (BOOL)isAutomaticSpellingCorrectionEnabled;
408 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag;
409 - (void)toggleAutomaticSpellingCorrection:(id)sender;
414 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
415 - (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageWidth logicalHeight:(float)minPageHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent;
416 - (void)_updateSecureInputState;
419 @class NSTextInputContext;
420 @interface NSResponder (AppKitDetails)
421 - (NSTextInputContext *)inputContext;
424 @interface NSObject (NSTextInputContextDetails)
425 - (BOOL)wantsToHandleMouseEvents;
426 - (BOOL)handleMouseEvent:(NSEvent *)event;
429 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
430 - (void)_updateSelectionForInputManager;
433 @interface WebHTMLView (WebEditingStyleSupport)
434 - (DOMCSSStyleDeclaration *)_emptyStyle;
435 - (NSString *)_colorAsString:(NSColor *)color;
438 @interface NSView (WebHTMLViewFileInternal)
439 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
442 @interface NSMutableDictionary (WebHTMLViewFileInternal)
443 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
446 struct WebHTMLViewInterpretKeyEventsParameters {
447 KeyboardEvent* event;
448 bool eventInterpretationHadSideEffects;
449 bool shouldSaveCommands;
451 bool executingSavedKeypressCommands;
454 @interface WebHTMLViewPrivate : NSObject {
457 BOOL ignoringMouseDraggedEvents;
459 BOOL paginateScreenContent;
460 BOOL observingMouseMovedNotifications;
461 BOOL observingSuperviewNotifications;
462 BOOL observingWindowNotifications;
465 BOOL subviewsSetAside;
467 #if USE(ACCELERATED_COMPOSITING)
468 NSView *layerHostingView;
469 BOOL drawingIntoLayer;
472 NSEvent *mouseDownEvent; // Kept after handling the event.
473 BOOL handlingMouseDownEvent;
474 NSEvent *keyDownEvent; // Kept after handling the event.
476 // A WebHTMLView has a single input context, but we return nil when in non-editable content to avoid making input methods do their work.
477 // This state is saved each time selection changes, because computing it causes style recalc, which is not always safe to do.
478 BOOL exposeInputContext;
480 // Track whether the view has set a secure input state.
481 BOOL isInSecureInputState;
483 BOOL _forceUpdateSecureInputState;
485 NSPoint lastScrollPosition;
486 BOOL inScrollPositionChanged;
488 WebPluginController *pluginController;
491 NSToolTipTag lastToolTipTag;
492 id trackingRectOwner;
493 void *trackingRectUserData;
495 NSTimer *autoscrollTimer;
496 NSEvent *autoscrollTriggerEvent;
500 NSMutableDictionary *highlighters;
503 WebTextCompletionController *completionController;
505 BOOL transparentBackground;
507 WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters;
509 WebDataSource *dataSource;
510 WebCore::CachedImage* promisedDragTIFFDataSource;
512 CFRunLoopTimerRef updateMouseoverTimer;
514 SEL selectorForDoCommandBySelector;
517 BOOL enumeratingSubviews;
523 static NSCellStateValue kit(TriState state)
533 ASSERT_NOT_REACHED();
537 @implementation WebHTMLViewPrivate
541 JSC::initializeThreading();
542 WTF::initializeMainThreadToProcessMainThread();
543 WebCoreObjCFinalizeOnMainThread(self);
545 if (!oldSetCursorForMouseLocationIMP) {
546 Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
547 ASSERT(setCursorMethod);
548 oldSetCursorForMouseLocationIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
549 ASSERT(oldSetCursorForMouseLocationIMP);
552 #if USE(ACCELERATED_COMPOSITING)
553 if (!oldSetNeedsDisplayInRectIMP) {
554 Method setNeedsDisplayInRectMethod = class_getInstanceMethod([NSView class], @selector(setNeedsDisplayInRect:));
555 ASSERT(setNeedsDisplayInRectMethod);
556 oldSetNeedsDisplayInRectIMP = method_setImplementation(setNeedsDisplayInRectMethod, (IMP)setNeedsDisplayInRect);
557 ASSERT(oldSetNeedsDisplayInRectIMP);
559 #endif // USE(ACCELERATED_COMPOSITING)
566 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self))
569 ASSERT(!autoscrollTimer);
570 ASSERT(!autoscrollTriggerEvent);
571 ASSERT(!updateMouseoverTimer);
573 [mouseDownEvent release];
574 [keyDownEvent release];
575 [pluginController release];
577 [completionController release];
578 [dataSource release];
579 [highlighters release];
580 if (promisedDragTIFFDataSource)
581 promisedDragTIFFDataSource->removeClient(promisedDataClient());
588 ASSERT_MAIN_THREAD();
590 if (promisedDragTIFFDataSource)
591 promisedDragTIFFDataSource->removeClient(promisedDataClient());
598 [mouseDownEvent release];
599 [keyDownEvent release];
600 [pluginController release];
602 [completionController release];
603 [dataSource release];
604 [highlighters release];
605 if (promisedDragTIFFDataSource)
606 promisedDragTIFFDataSource->removeClient(promisedDataClient());
608 mouseDownEvent = nil;
610 pluginController = nil;
612 completionController = nil;
615 promisedDragTIFFDataSource = 0;
617 #if USE(ACCELERATED_COMPOSITING)
618 layerHostingView = nil;
624 @implementation WebHTMLView (WebHTMLViewFileInternal)
626 - (DOMRange *)_documentRange
628 return [[[self _frame] DOMDocument] _documentRange];
631 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
633 NSEnumerator *enumerator = [paths objectEnumerator];
636 while ((path = [enumerator nextObject]) != nil) {
637 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
638 if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
645 - (WebDataSource *)_dataSource
647 return _private->dataSource;
650 - (WebView *)_webView
652 return [_private->dataSource _webView];
655 - (WebFrameView *)_frameView
657 return [[_private->dataSource webFrame] frameView];
660 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
662 DOMDocumentFragment *fragment;
663 NSEnumerator *enumerator = [paths objectEnumerator];
664 NSMutableArray *domNodes = [[NSMutableArray alloc] init];
667 while ((path = [enumerator nextObject]) != nil) {
668 // Non-image file types; _web_userVisibleString is appropriate here because this will
669 // be pasted as visible text.
670 NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
671 [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
674 fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes];
678 return [fragment firstChild] != nil ? fragment : nil;
681 + (NSArray *)_excludedElementsForAttributedStringConversion
683 static NSArray *elements = nil;
684 if (elements == nil) {
685 elements = [[NSArray alloc] initWithObjects:
686 // Omit style since we want style to be inline so the fragment can be easily inserted.
688 // Omit xml so the result is not XHTML.
690 // Omit tags that will get stripped when converted to a fragment anyway.
691 @"doctype", @"html", @"head", @"body",
692 // Omit deprecated tags.
693 @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
694 // Omit object so no file attachments are part of the fragment.
701 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
703 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
704 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
706 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
707 CFRelease(UUIDString);
712 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
713 inContext:(DOMRange *)context
714 allowPlainText:(BOOL)allowPlainText
716 NSArray *types = [pasteboard types];
717 DOMDocumentFragment *fragment = nil;
719 if ([types containsObject:WebArchivePboardType] &&
720 (fragment = [self _documentFragmentFromPasteboard:pasteboard
721 forType:WebArchivePboardType
726 if ([types containsObject:NSFilenamesPboardType] &&
727 (fragment = [self _documentFragmentFromPasteboard:pasteboard
728 forType:NSFilenamesPboardType
733 if ([types containsObject:NSHTMLPboardType] &&
734 (fragment = [self _documentFragmentFromPasteboard:pasteboard
735 forType:NSHTMLPboardType
740 if ([types containsObject:NSRTFDPboardType] &&
741 (fragment = [self _documentFragmentFromPasteboard:pasteboard
742 forType:NSRTFDPboardType
747 if ([types containsObject:NSRTFPboardType] &&
748 (fragment = [self _documentFragmentFromPasteboard:pasteboard
749 forType:NSRTFPboardType
754 if ([types containsObject:NSTIFFPboardType] &&
755 (fragment = [self _documentFragmentFromPasteboard:pasteboard
756 forType:NSTIFFPboardType
761 if ([types containsObject:NSPDFPboardType] &&
762 (fragment = [self _documentFragmentFromPasteboard:pasteboard
763 forType:NSPDFPboardType
768 #ifdef BUILDING_ON_LEOPARD
769 if ([types containsObject:NSPICTPboardType] &&
770 (fragment = [self _documentFragmentFromPasteboard:pasteboard
771 forType:NSPICTPboardType
777 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
778 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
779 if ([types containsObject:(NSString*)kUTTypePNG] &&
780 (fragment = [self _documentFragmentFromPasteboard:pasteboard
781 forType:(NSString*)kUTTypePNG
786 if ([types containsObject:NSURLPboardType] &&
787 (fragment = [self _documentFragmentFromPasteboard:pasteboard
788 forType:NSURLPboardType
793 if (allowPlainText && [types containsObject:NSStringPboardType] &&
794 (fragment = [self _documentFragmentFromPasteboard:pasteboard
795 forType:NSStringPboardType
804 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
806 NSArray *types = [pasteboard types];
808 if ([types containsObject:NSStringPboardType])
809 return [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping];
811 NSAttributedString *attributedString = nil;
814 if ([types containsObject:NSRTFDPboardType])
815 attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
816 if (attributedString == nil && [types containsObject:NSRTFPboardType])
817 attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
818 if (attributedString != nil) {
819 string = [[attributedString string] copy];
820 [attributedString release];
821 return [string autorelease];
824 if ([types containsObject:NSFilenamesPboardType]) {
825 string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
832 if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
833 string = [URL _web_userVisibleString];
834 if ([string length] > 0)
841 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
843 WebView *webView = [[self _webView] retain];
844 [webView _setInsertionPasteboard:pasteboard];
846 DOMRange *range = [self _selectedRange];
847 Frame* coreFrame = core([self _frame]);
849 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
850 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
851 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted])
852 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
854 // Mail is ignoring the frament passed to the delegate and creates a new one.
855 // We want to avoid creating the fragment twice.
856 if (applicationIsAppleMail()) {
857 if ([self _shouldInsertFragment:nil replacingDOMRange:range givenAction:WebViewInsertActionPasted]) {
858 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
860 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
863 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
864 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted])
865 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
868 [webView _setInsertionPasteboard:nil];
872 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
874 WebView *webView = [[self _webView] retain];
875 [webView _setInsertionPasteboard:pasteboard];
877 NSString *text = [self _plainTextFromPasteboard:pasteboard];
878 if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
879 [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
881 [webView _setInsertionPasteboard:nil];
885 // This method is needed to support Mac OS X services.
886 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
888 Frame* coreFrame = core([self _frame]);
891 if (coreFrame->selection()->isContentRichlyEditable())
892 [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
894 [self _pasteAsPlainTextWithPasteboard:pasteboard];
898 - (void)_removeMouseMovedObserverUnconditionally
900 if (!_private || !_private->observingMouseMovedNotifications)
903 [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil];
904 _private->observingMouseMovedNotifications = false;
907 - (void)_removeSuperviewObservers
909 if (!_private || !_private->observingSuperviewNotifications)
912 NSView *superview = [self superview];
913 if (!superview || ![self window])
916 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
917 [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview];
918 [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview];
920 _private->observingSuperviewNotifications = false;
923 - (void)_removeWindowObservers
925 if (!_private->observingWindowNotifications)
928 NSWindow *window = [self window];
932 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
933 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
934 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
935 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window];
937 _private->observingWindowNotifications = false;
940 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
942 WebView *webView = [self _webView];
943 DOMNode *child = [fragment firstChild];
944 if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
945 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
946 return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
949 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
951 WebView *webView = [self _webView];
952 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
955 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
957 return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
960 - (DOMRange *)_selectedRange
962 Frame* coreFrame = core([self _frame]);
963 return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil;
966 - (BOOL)_shouldDeleteRange:(DOMRange *)range
968 Frame* coreFrame = core([self _frame]);
969 return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
972 - (NSView *)_hitViewForEvent:(NSEvent *)event
974 // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.
975 // Callers of this method, however, want to query the deepest view instead.
976 forceNSViewHitTest = YES;
977 NSView *hitView = [(NSView *)[[self window] contentView] hitTest:[event locationInWindow]];
978 forceNSViewHitTest = NO;
982 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
984 // Put HTML on the pasteboard.
985 if ([types containsObject:WebArchivePboardType]) {
986 if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) {
987 if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0)
988 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType];
992 // Put the attributed string on the pasteboard (RTF/RTFD format).
993 if ([types containsObject:NSRTFDPboardType]) {
994 if (attributedString == nil) {
995 attributedString = [self selectedAttributedString];
997 NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
998 [pasteboard setData:RTFDData forType:NSRTFDPboardType];
1000 if ([types containsObject:NSRTFPboardType]) {
1001 if (!attributedString)
1002 attributedString = [self selectedAttributedString];
1003 if ([attributedString containsAttachments])
1004 attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
1005 NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
1006 [pasteboard setData:RTFData forType:NSRTFPboardType];
1009 // Put plain string on the pasteboard.
1010 if ([types containsObject:NSStringPboardType]) {
1011 // Map to a plain old space because this is better for source code, other browsers do it,
1012 // and because HTML forces you to do this any time you want two spaces in a row.
1013 NSMutableString *s = [[self selectedString] mutableCopy];
1014 const unichar NonBreakingSpaceCharacter = 0xA0;
1015 NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
1016 [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
1017 [pasteboard setString:s forType:NSStringPboardType];
1021 if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
1022 [pasteboard setData:nil forType:WebSmartPastePboardType];
1026 - (void)_setMouseDownEvent:(NSEvent *)event
1028 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
1030 if (event == _private->mouseDownEvent)
1034 [_private->mouseDownEvent release];
1035 _private->mouseDownEvent = event;
1038 - (void)_cancelUpdateMouseoverTimer
1040 if (_private->updateMouseoverTimer) {
1041 CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
1042 CFRelease(_private->updateMouseoverTimer);
1043 _private->updateMouseoverTimer = NULL;
1047 - (WebHTMLView *)_topHTMLView
1049 // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
1050 WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
1051 ASSERT(!view || [view isKindOfClass:[WebHTMLView class]]);
1055 - (BOOL)_isTopHTMLView
1057 // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
1058 return self == [self _topHTMLView];
1061 - (void)_web_setPrintingModeRecursive
1063 [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1066 _private->enumeratingSubviews = YES;
1069 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1071 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1073 unsigned count = [descendantWebHTMLViews count];
1074 for (unsigned i = 0; i < count; ++i)
1075 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1077 [descendantWebHTMLViews release];
1080 _private->enumeratingSubviews = NO;
1084 - (void)_web_clearPrintingModeRecursive
1086 [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1089 _private->enumeratingSubviews = YES;
1092 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1094 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1096 unsigned count = [descendantWebHTMLViews count];
1097 for (unsigned i = 0; i < count; ++i)
1098 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1100 [descendantWebHTMLViews release];
1103 _private->enumeratingSubviews = NO;
1107 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize
1109 [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
1112 _private->enumeratingSubviews = YES;
1115 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1117 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1119 unsigned count = [descendantWebHTMLViews count];
1120 for (unsigned i = 0; i < count; ++i)
1121 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
1123 [descendantWebHTMLViews release];
1126 _private->enumeratingSubviews = NO;
1132 @implementation WebHTMLView (WebPrivate)
1134 + (NSArray *)supportedMIMETypes
1136 return [WebHTMLRepresentation supportedMIMETypes];
1139 + (NSArray *)supportedImageMIMETypes
1141 return [WebHTMLRepresentation supportedImageMIMETypes];
1144 + (NSArray *)supportedNonImageMIMETypes
1146 return [WebHTMLRepresentation supportedNonImageMIMETypes];
1149 + (NSArray *)unsupportedTextMIMETypes
1151 return [WebHTMLRepresentation unsupportedTextMIMETypes];
1154 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
1156 // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged
1157 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1158 location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
1159 modifierFlags:[flagsChangedEvent modifierFlags]
1160 timestamp:[flagsChangedEvent timestamp]
1161 windowNumber:[flagsChangedEvent windowNumber]
1162 context:[flagsChangedEvent context]
1163 eventNumber:0 clickCount:0 pressure:0];
1165 // Pretend it's a mouse move.
1166 [[NSNotificationCenter defaultCenter]
1167 postNotificationName:WKMouseMovedNotification() object:self
1168 userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
1173 // This method exists to maintain compatibility with Leopard's Dictionary.app, since it
1174 // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:.
1175 // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160>
1176 return [self _frame];
1179 - (void)_updateMouseoverWithFakeEvent
1181 [self _cancelUpdateMouseoverTimer];
1183 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1184 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1185 modifierFlags:[[NSApp currentEvent] modifierFlags]
1186 timestamp:[NSDate timeIntervalSinceReferenceDate]
1187 windowNumber:[[self window] windowNumber]
1188 context:[[NSApp currentEvent] context]
1189 eventNumber:0 clickCount:0 pressure:0];
1191 [self _updateMouseoverWithEvent:fakeEvent];
1194 static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
1196 WebHTMLView *view = (WebHTMLView *)info;
1198 [view _updateMouseoverWithFakeEvent];
1201 - (void)_frameOrBoundsChanged
1203 WebView *webView = [self _webView];
1204 WebDynamicScrollBarsView *scrollView = [[[webView mainFrame] frameView] _scrollView];
1206 NSPoint origin = [[self superview] bounds].origin;
1207 if (!NSEqualPoints(_private->lastScrollPosition, origin) && ![scrollView inProgrammaticScroll]) {
1208 if (Frame* coreFrame = core([self _frame])) {
1209 if (FrameView* coreView = coreFrame->view()) {
1210 _private->inScrollPositionChanged = YES;
1211 coreView->scrollPositionChangedViaPlatformWidget();
1212 _private->inScrollPositionChanged = NO;
1216 [_private->completionController endRevertingChange:NO moveLeft:NO];
1218 [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
1220 _private->lastScrollPosition = origin;
1222 if ([self window] && !_private->closed && !_private->updateMouseoverTimer) {
1223 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
1225 // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading
1226 // and scrolling rapidly back to back.
1227 _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0,
1228 _updateMouseoverTimerCallback, &context);
1229 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode);
1232 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
1233 [self _updateLayerHostingViewPosition];
1237 - (void)_setAsideSubviews
1239 ASSERT(!_private->subviewsSetAside);
1240 ASSERT(_private->savedSubviews == nil);
1241 _private->savedSubviews = _subviews;
1242 #if USE(ACCELERATED_COMPOSITING)
1243 // We need to keep the layer-hosting view in the subviews, otherwise the layers flash.
1244 if (_private->layerHostingView) {
1245 NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil];
1246 _subviews = newSubviews;
1252 _private->subviewsSetAside = YES;
1255 - (void)_restoreSubviews
1257 ASSERT(_private->subviewsSetAside);
1258 #if USE(ACCELERATED_COMPOSITING)
1259 if (_private->layerHostingView) {
1260 [_subviews release];
1261 _subviews = _private->savedSubviews;
1263 ASSERT(_subviews == nil);
1264 _subviews = _private->savedSubviews;
1267 ASSERT(_subviews == nil);
1268 _subviews = _private->savedSubviews;
1270 _private->savedSubviews = nil;
1271 _private->subviewsSetAside = NO;
1276 - (void)didAddSubview:(NSView *)subview
1278 if (_private->enumeratingSubviews)
1279 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]));
1284 - (void)viewWillDraw
1286 // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1287 // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1288 // once the FIXME in _isTopHTMLView is fixed.
1289 if (_private->dataSource && [self _isTopHTMLView])
1290 [self _web_updateLayoutAndStyleIfNeededRecursive];
1291 [super viewWillDraw];
1295 // Don't let AppKit even draw subviews. We take care of that.
1296 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1298 // This helps when we print as part of a larger print process.
1299 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1300 BOOL wasInPrintingMode = _private->printing;
1301 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1303 if (!wasInPrintingMode)
1304 [self _web_setPrintingModeRecursive];
1306 [self _web_updateLayoutAndStyleIfNeededRecursive];
1307 } else if (wasInPrintingMode)
1308 [self _web_clearPrintingModeRecursive];
1310 // There are known cases where -viewWillDraw is not called on all views being drawn.
1311 // See <rdar://problem/6964278> for example. Performing layout at this point prevents us from
1312 // trying to paint without layout (which WebCore now refuses to do, instead bailing out without
1313 // drawing at all), but we may still fail to update any regions dirtied by the layout which are
1314 // not already dirty.
1315 if ([self _needsLayout]) {
1316 NSInteger rectCount;
1317 [self getRectsBeingDrawn:0 count:&rectCount];
1319 LOG_ERROR("View needs layout. Either -viewWillDraw wasn't called or layout was invalidated during the display operation. Performing layout now.");
1320 [self _web_updateLayoutAndStyleIfNeededRecursive];
1324 [self _setAsideSubviews];
1325 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1326 [self _restoreSubviews];
1328 if (wasInPrintingMode != isPrinting) {
1329 if (wasInPrintingMode)
1330 [self _web_setPrintingModeRecursive];
1332 [self _web_clearPrintingModeRecursive];
1336 // Don't let AppKit even draw subviews. We take care of that.
1337 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1339 BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1341 BOOL wasInPrintingMode = _private->printing;
1342 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1344 if (needToSetAsideSubviews) {
1345 // This helps when we print as part of a larger print process.
1346 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1348 if (!wasInPrintingMode)
1349 [self _web_setPrintingModeRecursive];
1351 [self _web_updateLayoutAndStyleIfNeededRecursive];
1352 } else if (wasInPrintingMode)
1353 [self _web_clearPrintingModeRecursive];
1356 [self _setAsideSubviews];
1359 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1361 if (needToSetAsideSubviews) {
1362 if (wasInPrintingMode != isPrinting) {
1363 if (wasInPrintingMode)
1364 [self _web_setPrintingModeRecursive];
1366 [self _web_clearPrintingModeRecursive];
1369 [self _restoreSubviews];
1373 // Don't let AppKit even draw subviews. We take care of that.
1374 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView
1377 [self _setAsideSubviews];
1378 [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView];
1379 [self _restoreSubviews];
1382 - (BOOL)_insideAnotherHTMLView
1384 return self != [self _topHTMLView];
1387 - (NSView *)hitTest:(NSPoint)point
1389 // WebHTMLView objects handle all events for objects inside them.
1390 // To get those events, we prevent hit testing from AppKit.
1392 // But there are three exceptions to this:
1393 // 1) For right mouse clicks and control clicks we don't yet have an implementation
1394 // that works for nested views, so we let the hit testing go through the
1395 // standard NSView code path (needs to be fixed, see bug 4361618).
1396 // 2) Java depends on doing a hit test inside it's mouse moved handling,
1397 // so we let the hit testing go through the standard NSView code path
1398 // when the current event is a mouse move (except when we are calling
1399 // from _updateMouseoverWithEvent, so we have to use a global,
1400 // forceWebHTMLViewHitTest, for that)
1401 // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1402 // both need to figure out which view to check with inside the WebHTMLView.
1403 // They use a global to change the behavior of hitTest: so they can get the
1404 // right view. The global is forceNSViewHitTest and the method they use to
1405 // do the hit testing is _hitViewForEvent:. (But this does not work correctly
1406 // when there is HTML overlapping the view, see bug 4361626)
1407 // 4) NSAccessibilityHitTest relies on this for checking the cursor position.
1408 // Our check for that is whether the event is NSFlagsChanged. This works
1409 // for VoiceOver's Control-Option-F5 command (move focus to item under cursor)
1410 // and Dictionary's Command-Control-D (open dictionary popup for item under cursor).
1411 // This is of course a hack.
1413 if (_private->closed)
1416 BOOL captureHitsOnSubviews;
1417 if (forceNSViewHitTest)
1418 captureHitsOnSubviews = NO;
1419 else if (forceWebHTMLViewHitTest)
1420 captureHitsOnSubviews = YES;
1422 // FIXME: Why doesn't this include mouse entered/exited events, or other mouse button events?
1423 NSEvent *event = [[self window] currentEvent];
1424 captureHitsOnSubviews = !([event type] == NSMouseMoved
1425 || [event type] == NSRightMouseDown
1426 || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1427 || [event type] == NSFlagsChanged);
1430 if (!captureHitsOnSubviews) {
1431 NSView* hitView = [super hitTest:point];
1432 #if USE(ACCELERATED_COMPOSITING)
1433 if (_private && hitView == _private->layerHostingView)
1438 if ([[self superview] mouse:point inRect:[self frame]])
1443 - (void)_clearLastHitViewIfSelf
1445 if (lastHitView == self)
1449 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1451 ASSERT(_private->trackingRectOwner == nil);
1452 _private->trackingRectOwner = owner;
1453 _private->trackingRectUserData = data;
1454 return TRACKING_RECT_TAG;
1457 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1459 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1460 ASSERT(_private->trackingRectOwner == nil);
1461 _private->trackingRectOwner = owner;
1462 _private->trackingRectUserData = data;
1463 return TRACKING_RECT_TAG;
1466 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1469 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1470 ASSERT(_private->trackingRectOwner == nil);
1471 _private->trackingRectOwner = owner;
1472 _private->trackingRectUserData = userDataList[0];
1473 trackingNums[0] = TRACKING_RECT_TAG;
1476 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1481 if (_private && (tag == TRACKING_RECT_TAG)) {
1482 _private->trackingRectOwner = nil;
1486 if (_private && (tag == _private->lastToolTipTag)) {
1487 [super removeTrackingRect:tag];
1488 _private->lastToolTipTag = 0;
1492 // If any other tracking rect is being removed, we don't know how it was created
1493 // and it's possible there's a leak involved (see 3500217)
1494 ASSERT_NOT_REACHED();
1497 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1500 for (i = 0; i < count; ++i) {
1504 ASSERT(tag == TRACKING_RECT_TAG);
1505 if (_private != nil) {
1506 _private->trackingRectOwner = nil;
1511 - (void)_sendToolTipMouseExited
1513 // Nothing matters except window, trackingNumber, and userData.
1514 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1515 location:NSMakePoint(0, 0)
1518 windowNumber:[[self window] windowNumber]
1521 trackingNumber:TRACKING_RECT_TAG
1522 userData:_private->trackingRectUserData];
1523 [_private->trackingRectOwner mouseExited:fakeEvent];
1526 - (void)_sendToolTipMouseEntered
1528 // Nothing matters except window, trackingNumber, and userData.
1529 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1530 location:NSMakePoint(0, 0)
1533 windowNumber:[[self window] windowNumber]
1536 trackingNumber:TRACKING_RECT_TAG
1537 userData:_private->trackingRectUserData];
1538 [_private->trackingRectOwner mouseEntered:fakeEvent];
1541 - (void)_setToolTip:(NSString *)string
1543 NSString *toolTip = [string length] == 0 ? nil : string;
1544 NSString *oldToolTip = _private->toolTip;
1545 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1549 [self _sendToolTipMouseExited];
1550 [oldToolTip release];
1552 _private->toolTip = [toolTip copy];
1554 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1555 [self removeAllToolTips];
1556 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1557 _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1558 [self _sendToolTipMouseEntered];
1562 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1564 return [[_private->toolTip copy] autorelease];
1567 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1569 if (_private->closed)
1572 NSView *contentView = [[event window] contentView];
1573 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1575 forceWebHTMLViewHitTest = YES;
1576 NSView *hitView = [contentView hitTest:locationForHitTest];
1577 forceWebHTMLViewHitTest = NO;
1579 WebHTMLView *view = nil;
1580 if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
1581 view = (WebHTMLView *)hitView;
1586 if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1587 // If we are moving out of a view (or frame), let's pretend the mouse moved
1588 // all the way out of that view. But we have to account for scrolling, because
1589 // WebCore doesn't understand our clipping.
1590 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1591 float yScroll = visibleRect.origin.y;
1592 float xScroll = visibleRect.origin.x;
1594 NSEvent *event = [NSEvent mouseEventWithType:NSMouseMoved
1595 location:NSMakePoint(-1 - xScroll, -1 - yScroll)
1596 modifierFlags:[[NSApp currentEvent] modifierFlags]
1597 timestamp:[NSDate timeIntervalSinceReferenceDate]
1598 windowNumber:[[view window] windowNumber]
1599 context:[[NSApp currentEvent] context]
1600 eventNumber:0 clickCount:0 pressure:0];
1601 if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1602 lastHitCoreFrame->eventHandler()->mouseMoved(event);
1608 if (Frame* coreFrame = core([view _frame]))
1609 coreFrame->eventHandler()->mouseMoved(event);
1615 + (NSArray *)_insertablePasteboardTypes
1617 static NSArray *types = nil;
1619 types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType,
1620 #ifdef BUILDING_ON_LEOPARD
1623 NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
1629 + (NSArray *)_selectionPasteboardTypes
1631 // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1632 return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1635 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1637 [self setPromisedDragTIFFDataSource:0];
1640 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1642 if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1643 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1644 [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1646 } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1647 if (Image* image = [self promisedDragTIFFDataSource]->image())
1648 [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1649 [self setPromisedDragTIFFDataSource:0];
1653 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
1655 [self autoscroll:event];
1656 [self _startAutoscrollTimer:event];
1659 - (WebPluginController *)_pluginController
1661 return _private->pluginController;
1664 - (void)_layoutForPrinting
1666 // Set printing mode temporarily so we can adjust the size of the view. This will allow
1667 // AppKit's pagination code to use the correct height for the page content. Leaving printing
1668 // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1669 // turn it off again after adjusting the size.
1670 [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1671 [self _web_clearPrintingModeRecursive];
1674 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1676 if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
1678 *beforeString = nil;
1684 [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
1687 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
1689 return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
1692 - (void)_startAutoscrollTimer:(NSEvent *)triggerEvent
1694 if (_private->autoscrollTimer == nil) {
1695 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1696 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1697 _private->autoscrollTriggerEvent = [triggerEvent retain];
1701 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1702 // We can't remove this yet because it's still in use by Mail.
1703 - (NSRect)_selectionRect
1705 return [self selectionRect];
1708 - (void)_stopAutoscrollTimer
1710 NSTimer *timer = _private->autoscrollTimer;
1711 _private->autoscrollTimer = nil;
1712 [_private->autoscrollTriggerEvent release];
1713 _private->autoscrollTriggerEvent = nil;
1720 // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1721 // a mouse up event.
1722 BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);
1724 [self _stopAutoscrollTimer];
1728 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1729 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1730 modifierFlags:[[NSApp currentEvent] modifierFlags]
1731 timestamp:[NSDate timeIntervalSinceReferenceDate]
1732 windowNumber:[[self window] windowNumber]
1733 context:[[NSApp currentEvent] context]
1734 eventNumber:0 clickCount:0 pressure:0];
1735 [self mouseDragged:fakeEvent];
1740 Frame* coreFrame = core([self _frame]);
1741 return coreFrame && coreFrame->editor()->canEdit();
1744 - (BOOL)_canEditRichly
1746 Frame* coreFrame = core([self _frame]);
1747 return coreFrame && coreFrame->editor()->canEditRichly();
1750 - (BOOL)_canAlterCurrentSelection
1752 return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1755 - (BOOL)_hasSelection
1757 Frame* coreFrame = core([self _frame]);
1758 return coreFrame && coreFrame->selection()->isRange();
1761 - (BOOL)_hasSelectionOrInsertionPoint
1763 Frame* coreFrame = core([self _frame]);
1764 return coreFrame && coreFrame->selection()->isCaretOrRange();
1767 - (BOOL)_hasInsertionPoint
1769 Frame* coreFrame = core([self _frame]);
1770 return coreFrame && coreFrame->selection()->isCaret();
1775 Frame* coreFrame = core([self _frame]);
1776 return coreFrame && coreFrame->selection()->isContentEditable();
1779 - (BOOL)_transparentBackground
1781 return _private->transparentBackground;
1784 - (void)_setTransparentBackground:(BOOL)f
1786 _private->transparentBackground = f;
1789 - (NSImage *)_selectionDraggingImage
1791 if (![self _hasSelection])
1793 NSImage *dragImage = core([self _frame])->selectionImage();
1794 [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1798 - (NSRect)_selectionDraggingRect
1800 // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1801 return [self selectionRect];
1804 - (DOMNode *)_insertOrderedList
1806 Frame* coreFrame = core([self _frame]);
1807 return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
1810 - (DOMNode *)_insertUnorderedList
1812 Frame* coreFrame = core([self _frame]);
1813 return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
1816 - (BOOL)_canIncreaseSelectionListLevel
1818 Frame* coreFrame = core([self _frame]);
1819 return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
1822 - (BOOL)_canDecreaseSelectionListLevel
1824 Frame* coreFrame = core([self _frame]);
1825 return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
1828 - (DOMNode *)_increaseSelectionListLevel
1830 Frame* coreFrame = core([self _frame]);
1831 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
1834 - (DOMNode *)_increaseSelectionListLevelOrdered
1836 Frame* coreFrame = core([self _frame]);
1837 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
1840 - (DOMNode *)_increaseSelectionListLevelUnordered
1842 Frame* coreFrame = core([self _frame]);
1843 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
1846 - (void)_decreaseSelectionListLevel
1848 Frame* coreFrame = core([self _frame]);
1850 coreFrame->editor()->decreaseSelectionListLevel();
1853 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1855 if (!_private->highlighters)
1856 _private->highlighters = [[NSMutableDictionary alloc] init];
1857 [_private->highlighters setObject:highlighter forKey:type];
1860 - (void)_removeHighlighterOfType:(NSString*)type
1862 [_private->highlighters removeObjectForKey:type];
1865 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1867 ASSERT([self _hasSelection]);
1868 NSArray *types = [self pasteboardTypesForSelection];
1870 // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1871 NSAttributedString *attributedString = [self selectedAttributedString];
1872 NSMutableArray *mutableTypes = nil;
1873 if (![attributedString containsAttachments]) {
1874 mutableTypes = [types mutableCopy];
1875 [mutableTypes removeObject:NSRTFDPboardType];
1876 types = mutableTypes;
1879 [pasteboard declareTypes:types owner:[self _topHTMLView]];
1880 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1881 [mutableTypes release];
1886 // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing
1887 // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
1888 if (!_private || _private->closed)
1891 _private->closed = YES;
1893 [self _cancelUpdateMouseoverTimer];
1894 [self _clearLastHitViewIfSelf];
1895 [self _removeMouseMovedObserverUnconditionally];
1896 [self _removeWindowObservers];
1897 [self _removeSuperviewObservers];
1898 [_private->pluginController destroyAllPlugins];
1899 [_private->pluginController setDataSource:nil];
1900 // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1901 [self removeAllToolTips];
1903 if (_private->isInSecureInputState) {
1904 DisableSecureEventInput();
1905 _private->isInSecureInputState = NO;
1911 - (BOOL)_hasHTMLDocument
1913 Frame* coreFrame = core([self _frame]);
1916 Document* document = coreFrame->document();
1917 return document && document->isHTMLDocument();
1920 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1921 forType:(NSString *)pboardType
1922 inContext:(DOMRange *)context
1923 subresources:(NSArray **)subresources
1925 if (pboardType == WebArchivePboardType) {
1926 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1928 *subresources = [archive subresources];
1929 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1933 if (pboardType == NSFilenamesPboardType)
1934 return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1936 if (pboardType == NSHTMLPboardType) {
1937 NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1938 // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1939 if ([HTMLString hasPrefix:@"Version:"]) {
1940 NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
1941 if (range.location != NSNotFound)
1942 HTMLString = [HTMLString substringFromIndex:range.location];
1944 if ([HTMLString length] == 0)
1947 return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
1950 // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
1951 // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
1952 // FIXME: Remove this once bug 5052369 is fixed.
1953 if ([self _hasHTMLDocument] && (pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType)) {
1954 NSAttributedString *string = nil;
1955 if (pboardType == NSRTFDPboardType)
1956 string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
1958 string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
1962 NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
1963 [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
1964 self, @"WebResourceHandler", nil];
1967 BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
1968 if (!wasDeferringCallbacks)
1969 [[self _webView] setDefersCallbacks:YES];
1971 DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length])
1972 document:[[self _frame] DOMDocument]
1973 documentAttributes:documentAttributes
1978 NSEnumerator *e = [s objectEnumerator];
1980 while ((r = [e nextObject]))
1981 [[self _dataSource] addSubresource:r];
1983 if (!wasDeferringCallbacks)
1984 [[self _webView] setDefersCallbacks:NO];
1986 [documentAttributes release];
1990 if (pboardType == NSTIFFPboardType) {
1991 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
1992 URL:uniqueURLWithRelativePart(@"image.tiff")
1993 MIMEType:@"image/tiff"
1994 textEncodingName:nil
1996 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2000 if (pboardType == NSPDFPboardType) {
2001 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPDFPboardType]
2002 URL:uniqueURLWithRelativePart(@"application.pdf")
2003 MIMEType:@"application/pdf"
2004 textEncodingName:nil
2006 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2010 #ifdef BUILDING_ON_LEOPARD
2011 if (pboardType == NSPICTPboardType) {
2012 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
2013 URL:uniqueURLWithRelativePart(@"image.pict")
2014 MIMEType:@"image/pict"
2015 textEncodingName:nil
2017 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2022 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
2023 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
2024 if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) {
2025 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG]
2026 URL:uniqueURLWithRelativePart(@"image.png")
2027 MIMEType:@"image/png"
2028 textEncodingName:nil
2030 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2034 if (pboardType == NSURLPboardType) {
2035 NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
2036 DOMDocument* document = [[self _frame] DOMDocument];
2040 DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
2041 NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose.
2042 if ([URLString length] == 0)
2044 NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping];
2045 DOMText *text = [document createTextNode:URLTitleString];
2046 [anchor setHref:URLString];
2047 [anchor appendChild:text];
2048 DOMDocumentFragment *fragment = [document createDocumentFragment];
2049 [fragment appendChild:anchor];
2052 if (pboardType == NSStringPboardType)
2053 return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get());
2057 #if ENABLE(NETSCAPE_PLUGIN_API)
2058 - (void)_pauseNullEventsForAllNetscapePlugins
2060 NSArray *subviews = [self subviews];
2061 unsigned int subviewCount = [subviews count];
2062 unsigned int subviewIndex;
2064 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2065 NSView *subview = [subviews objectAtIndex:subviewIndex];
2066 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2067 [(WebBaseNetscapePluginView *)subview stopTimers];
2072 #if ENABLE(NETSCAPE_PLUGIN_API)
2073 - (void)_resumeNullEventsForAllNetscapePlugins
2075 NSArray *subviews = [self subviews];
2076 unsigned int subviewCount = [subviews count];
2077 unsigned int subviewIndex;
2079 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2080 NSView *subview = [subviews objectAtIndex:subviewIndex];
2081 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2082 [(WebBaseNetscapePluginView *)subview restartTimers];
2087 - (BOOL)_isUsingAcceleratedCompositing
2089 #if USE(ACCELERATED_COMPOSITING)
2090 return _private->layerHostingView != nil;
2096 - (NSView *)_compositingLayersHostingView
2098 #if USE(ACCELERATED_COMPOSITING)
2099 return _private->layerHostingView;
2105 - (BOOL)_isInPrintMode
2107 return _private->printing;
2110 - (BOOL)_beginPrintModeWithMinimumPageWidth:(CGFloat)minimumPageWidth height:(CGFloat)minimumPageHeight maximumPageWidth:(CGFloat)maximumPageWidth
2112 Frame* frame = core([self _frame]);
2116 if (frame->document() && frame->document()->isFrameSet()) {
2117 minimumPageWidth = 0;
2118 minimumPageHeight = 0;
2121 float maximumShrinkRatio = 0;
2122 if (minimumPageWidth > 0.0)
2123 maximumShrinkRatio = maximumPageWidth / minimumPageWidth;
2125 [self _setPrinting:YES minimumPageLogicalWidth:minimumPageWidth logicalHeight:minimumPageHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2129 - (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth height:(float)pageHeight shrinkToFit:(BOOL)shrinkToFit
2131 Frame* frame = core([self _frame]);
2135 Document* document = frame->document();
2136 bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
2138 float pageLogicalWidth = isHorizontal ? pageWidth : pageHeight;
2139 float pageLogicalHeight = isHorizontal ? pageHeight : pageWidth;
2140 FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2141 float maximumShrinkRatio = 1;
2143 // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2144 // according to the page width.
2145 if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2146 minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2147 maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2150 [self _setPrinting:YES minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2155 - (void)_endPrintMode
2157 [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2160 - (BOOL)_isInScreenPaginationMode
2162 return _private->paginateScreenContent;
2165 - (BOOL)_beginScreenPaginationModeWithPageSize:(CGSize)pageSize shrinkToFit:(BOOL)shrinkToFit
2167 Frame* frame = core([self _frame]);
2171 Document* document = frame->document();
2172 bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
2174 float pageLogicalWidth = isHorizontal ? pageSize.width : pageSize.height;
2175 float pageLogicalHeight = isHorizontal ? pageSize.height : pageSize.width;
2176 FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2177 float maximumShrinkRatio = 1;
2179 // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2180 // according to the page width.
2181 if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2182 minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2183 maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2186 [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2191 - (void)_endScreenPaginationMode
2193 [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:NO];
2196 - (CGFloat)_adjustedBottomOfPageWithTop:(CGFloat)top bottom:(CGFloat)bottom limit:(CGFloat)bottomLimit
2198 Frame* frame = core([self _frame]);
2202 FrameView* view = frame->view();
2207 view->adjustPageHeightDeprecated(&newBottom, top, bottom, bottomLimit);
2210 // If the new bottom is equal to the old bottom (when both are treated as floats), we just return the original
2211 // bottom. This prevents rounding errors that can occur when converting newBottom to a double.
2212 if (fabs(static_cast<float>(bottom) - newBottom) <= numeric_limits<float>::epsilon())
2221 @implementation NSView (WebHTMLViewFileInternal)
2223 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
2225 unsigned count = [_subviews count];
2226 for (unsigned i = 0; i < count; ++i) {
2227 NSView *child = [_subviews objectAtIndex:i];
2228 if ([child isKindOfClass:[WebHTMLView class]])
2229 [array addObject:child];
2230 [child _web_addDescendantWebHTMLViewsToArray:array];
2236 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
2238 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
2240 if (object == nil) {
2241 [self removeObjectForKey:key];
2243 [self setObject:object forKey:key];
2249 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
2251 NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
2252 return [filename _webkit_hasCaseInsensitiveSuffix:extensionAsSuffix]
2253 || ([extension _webkit_isCaseInsensitiveEqualToString:@"jpeg"]
2254 && [filename _webkit_hasCaseInsensitiveSuffix:@".jpg"]);
2258 @implementation WebHTMLView
2262 [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes]
2263 returnTypes:[[self class] _insertablePasteboardTypes]];
2264 JSC::initializeThreading();
2265 WTF::initializeMainThreadToProcessMainThread();
2266 WebCoreObjCFinalizeOnMainThread(self);
2269 - (id)initWithFrame:(NSRect)frame
2271 self = [super initWithFrame:frame];
2275 [self setFocusRingType:NSFocusRingTypeNone];
2277 // Make all drawing go through us instead of subviews.
2278 [self _setDrawsOwnDescendants:YES];
2280 _private = [[WebHTMLViewPrivate alloc] init];
2282 _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2289 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self))
2292 // We can't assert that close has already been called because
2293 // this view can be removed from it's superview, even though
2294 // it could be needed later, so close if needed.
2303 ASSERT_MAIN_THREAD();
2304 // We can't assert that close has already been called because
2305 // this view can be removed from it's superview, even though
2306 // it could be needed later, so close if needed.
2311 // Returns YES if the delegate returns YES (so we should do no more work).
2312 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2314 BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2315 _private->selectorForDoCommandBySelector = 0;
2316 if (callerAlreadyCalledDelegate)
2318 WebView *webView = [self _webView];
2319 return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2322 typedef HashMap<SEL, String> SelectorNameMap;
2324 // Map selectors into Editor command names.
2325 // This is not needed for any selectors that have the same name as the Editor command.
2326 static const SelectorNameMap* createSelectorExceptionMap()
2328 SelectorNameMap* map = new HashMap<SEL, String>;
2330 map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline");
2331 map->add(@selector(insertParagraphSeparator:), "InsertNewline");
2332 map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab");
2333 map->add(@selector(pageDown:), "MovePageDown");
2334 map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection");
2335 map->add(@selector(pageUp:), "MovePageUp");
2336 map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection");
2341 static String commandNameForSelector(SEL selector)
2343 // Check the exception map first.
2344 static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
2345 SelectorNameMap::const_iterator it = exceptionMap->find(selector);
2346 if (it != exceptionMap->end())
2349 // Remove the trailing colon.
2350 // No need to capitalize the command name since Editor command names are
2351 // not case sensitive.
2352 const char* selectorName = sel_getName(selector);
2353 size_t selectorNameLength = strlen(selectorName);
2354 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2356 return String(selectorName, selectorNameLength - 1);
2359 - (Editor::Command)coreCommandBySelector:(SEL)selector
2361 Frame* coreFrame = core([self _frame]);
2363 return Editor::Command();
2364 return coreFrame->editor()->command(commandNameForSelector(selector));
2367 - (Editor::Command)coreCommandByName:(const char*)name
2369 Frame* coreFrame = core([self _frame]);
2371 return Editor::Command();
2372 return coreFrame->editor()->command(name);
2375 - (void)executeCoreCommandBySelector:(SEL)selector
2377 if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2379 [self coreCommandBySelector:selector].execute();
2382 - (void)executeCoreCommandByName:(const char*)name
2384 [self coreCommandByName:name].execute();
2387 // These commands are forwarded to the Editor object in WebCore.
2388 // Ideally we'd do this for all editing commands; more of the code
2389 // should be moved from here to there, and more commands should be
2390 // added to this list.
2392 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2393 // The functions are identical.
2395 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2397 WEBCORE_COMMAND(alignCenter)
2398 WEBCORE_COMMAND(alignJustified)
2399 WEBCORE_COMMAND(alignLeft)
2400 WEBCORE_COMMAND(alignRight)
2401 WEBCORE_COMMAND(copy)
2402 WEBCORE_COMMAND(cut)
2403 WEBCORE_COMMAND(paste)
2404 WEBCORE_COMMAND(delete)
2405 WEBCORE_COMMAND(deleteBackward)
2406 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2407 WEBCORE_COMMAND(deleteForward)
2408 WEBCORE_COMMAND(deleteToBeginningOfLine)
2409 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2410 WEBCORE_COMMAND(deleteToEndOfLine)
2411 WEBCORE_COMMAND(deleteToEndOfParagraph)
2412 WEBCORE_COMMAND(deleteToMark)
2413 WEBCORE_COMMAND(deleteWordBackward)
2414 WEBCORE_COMMAND(deleteWordForward)
2415 WEBCORE_COMMAND(ignoreSpelling)
2416 WEBCORE_COMMAND(indent)
2417 WEBCORE_COMMAND(insertBacktab)
2418 WEBCORE_COMMAND(insertLineBreak)
2419 WEBCORE_COMMAND(insertNewline)
2420 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2421 WEBCORE_COMMAND(insertParagraphSeparator)
2422 WEBCORE_COMMAND(insertTab)
2423 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2424 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
2425 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
2426 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
2427 WEBCORE_COMMAND(moveBackward)
2428 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2429 WEBCORE_COMMAND(moveDown)
2430 WEBCORE_COMMAND(moveDownAndModifySelection)
2431 WEBCORE_COMMAND(moveForward)
2432 WEBCORE_COMMAND(moveForwardAndModifySelection)
2433 WEBCORE_COMMAND(moveLeft)
2434 WEBCORE_COMMAND(moveLeftAndModifySelection)
2435 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2436 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2437 WEBCORE_COMMAND(moveRight)
2438 WEBCORE_COMMAND(moveRightAndModifySelection)
2439 WEBCORE_COMMAND(moveToBeginningOfDocument)
2440 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2441 WEBCORE_COMMAND(moveToBeginningOfLine)
2442 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2443 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2444 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2445 WEBCORE_COMMAND(moveToBeginningOfSentence)
2446 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2447 WEBCORE_COMMAND(moveToEndOfDocument)
2448 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2449 WEBCORE_COMMAND(moveToEndOfLine)
2450 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2451 WEBCORE_COMMAND(moveToEndOfParagraph)
2452 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2453 WEBCORE_COMMAND(moveToEndOfSentence)
2454 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2455 WEBCORE_COMMAND(moveToLeftEndOfLine)
2456 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
2457 WEBCORE_COMMAND(moveToRightEndOfLine)
2458 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
2459 WEBCORE_COMMAND(moveUp)
2460 WEBCORE_COMMAND(moveUpAndModifySelection)
2461 WEBCORE_COMMAND(moveWordBackward)
2462 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2463 WEBCORE_COMMAND(moveWordForward)
2464 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2465 WEBCORE_COMMAND(moveWordLeft)
2466 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2467 WEBCORE_COMMAND(moveWordRight)
2468 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2469 WEBCORE_COMMAND(outdent)
2470 WEBCORE_COMMAND(pageDown)
2471 WEBCORE_COMMAND(pageDownAndModifySelection)
2472 WEBCORE_COMMAND(pageUp)
2473 WEBCORE_COMMAND(pageUpAndModifySelection)
2474 WEBCORE_COMMAND(pasteAsPlainText)
2475 WEBCORE_COMMAND(selectAll)
2476 WEBCORE_COMMAND(selectLine)
2477 WEBCORE_COMMAND(selectParagraph)
2478 WEBCORE_COMMAND(selectSentence)
2479 WEBCORE_COMMAND(selectToMark)
2480 WEBCORE_COMMAND(selectWord)
2481 WEBCORE_COMMAND(setMark)
2482 WEBCORE_COMMAND(subscript)
2483 WEBCORE_COMMAND(superscript)
2484 WEBCORE_COMMAND(swapWithMark)
2485 WEBCORE_COMMAND(transpose)
2486 WEBCORE_COMMAND(underline)
2487 WEBCORE_COMMAND(unscript)
2488 WEBCORE_COMMAND(yank)
2489 WEBCORE_COMMAND(yankAndSelect)
2491 #undef WEBCORE_COMMAND
2493 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2495 - (IBAction)takeFindStringFromSelection:(id)sender
2499 if (![self _hasSelection]) {
2504 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2507 // This method is needed to support Mac OS X services.
2508 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2510 [pasteboard declareTypes:types owner:[self _topHTMLView]];
2511 [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2515 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2517 BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]);
2518 BOOL isReturnTypeOK = NO;
2520 isReturnTypeOK = YES;
2521 else if ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2522 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts.
2523 isReturnTypeOK = [returnType isEqualToString:NSStringPboardType] || [self _canEditRichly];
2525 if (isSendTypeOK && isReturnTypeOK)
2527 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2530 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2531 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2532 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2533 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2534 // might be using the selector, and we don't want to break them.
2535 - (void)jumpToSelection:(id)sender
2539 if (Frame* coreFrame = core([self _frame]))
2540 coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways);
2543 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2545 SEL action = [item action];
2546 RefPtr<Frame> frame = core([self _frame]);
2551 if (Document* doc = frame->document()) {
2552 if (doc->isPluginDocument())
2554 if (doc->isImageDocument()) {
2555 if (action == @selector(copy:))
2556 return frame->loader()->isComplete();
2561 if (action == @selector(changeSpelling:)
2562 || action == @selector(_changeSpellingFromMenu:)
2563 || action == @selector(checkSpelling:)
2564 || action == @selector(complete:)
2565 || action == @selector(pasteFont:))
2566 return [self _canEdit];
2568 if (action == @selector(showGuessPanel:)) {
2569 // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2570 NSMenuItem *menuItem = (NSMenuItem *)item;
2571 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2572 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2573 [menuItem setTitle:panelShowing
2574 ? UI_STRING_INTERNAL("Hide Spelling and Grammar", "menu item title")
2575 : UI_STRING_INTERNAL("Show Spelling and Grammar", "menu item title")];
2577 return [self _canEdit];
2580 if (action == @selector(changeBaseWritingDirection:)
2581 || action == @selector(makeBaseWritingDirectionLeftToRight:)
2582 || action == @selector(makeBaseWritingDirectionRightToLeft:)) {
2583 NSWritingDirection writingDirection;
2585 if (action == @selector(changeBaseWritingDirection:)) {
2586 writingDirection = static_cast<NSWritingDirection>([item tag]);
2587 if (writingDirection == NSWritingDirectionNatural)
2589 } else if (action == @selector(makeBaseWritingDirectionLeftToRight:))
2590 writingDirection = NSWritingDirectionLeftToRight;
2592 writingDirection = NSWritingDirectionRightToLeft;
2594 NSMenuItem *menuItem = (NSMenuItem *)item;
2595 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2596 String direction = writingDirection == NSWritingDirectionLeftToRight ? "ltr" : "rtl";
2597 [menuItem setState:frame->editor()->selectionHasStyle(CSSPropertyDirection, direction)];
2599 return [self _canEdit];
2602 if (action == @selector(makeBaseWritingDirectionNatural:)) {
2603 NSMenuItem *menuItem = (NSMenuItem *)item;
2604 if ([menuItem isKindOfClass:[NSMenuItem class]])
2605 [menuItem setState:NSOffState];
2609 if (action == @selector(toggleBaseWritingDirection:)) {
2610 NSMenuItem *menuItem = (NSMenuItem *)item;
2611 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2612 // Take control of the title of the menu item instead of just checking/unchecking it because
2613 // a check would be ambiguous.
2614 [menuItem setTitle:frame->editor()->selectionHasStyle(CSSPropertyDirection, "rtl")
2615 ? UI_STRING_INTERNAL("Left to Right", "Left to Right context menu item")
2616 : UI_STRING_INTERNAL("Right to Left", "Right to Left context menu item")];
2618 return [self _canEdit];
2621 if (action == @selector(changeAttributes:)
2622 || action == @selector(changeColor:)
2623 || action == @selector(changeFont:))
2624 return [self _canEditRichly];
2626 if (action == @selector(capitalizeWord:)
2627 || action == @selector(lowercaseWord:)
2628 || action == @selector(uppercaseWord:))
2629 return [self _hasSelection] && [self _isEditable];
2631 if (action == @selector(centerSelectionInVisibleArea:)
2632 || action == @selector(jumpToSelection:)
2633 || action == @selector(copyFont:))
2634 return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2636 if (action == @selector(changeDocumentBackgroundColor:))
2637 return [[self _webView] isEditable] && [self _canEditRichly];
2639 if (action == @selector(_ignoreSpellingFromMenu:)
2640 || action == @selector(_learnSpellingFromMenu:)
2641 || action == @selector(takeFindStringFromSelection:))
2642 return [self _hasSelection];
2644 if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2645 return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste());
2647 if (action == @selector(pasteAsRichText:))
2648 return frame && (frame->editor()->canDHTMLPaste()
2649 || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable()));
2651 if (action == @selector(performFindPanelAction:))
2654 if (action == @selector(_lookUpInDictionaryFromMenu:))
2655 return [self _hasSelection];
2657 if (action == @selector(stopSpeaking:))
2658 return [NSApp isSpeaking];
2660 if (action == @selector(toggleGrammarChecking:)) {
2661 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate
2662 // the selector here because we implement it here, and we must implement it here because the AppKit
2663 // code checks the first responder.
2664 NSMenuItem *menuItem = (NSMenuItem *)item;
2665 if ([menuItem isKindOfClass:[NSMenuItem class]])
2666 [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2670 #ifndef BUILDING_ON_LEOPARD
2671 if (action == @selector(orderFrontSubstitutionsPanel:)) {
2672 NSMenuItem *menuItem = (NSMenuItem *)item;
2673 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2674 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
2675 [menuItem setTitle:panelShowing
2676 ? UI_STRING_INTERNAL("Hide Substitutions", "menu item title")
2677 : UI_STRING_INTERNAL("Show Substitutions", "menu item title")];
2679 return [self _canEdit];
2681 // FIXME 4799134: WebView is the bottleneck for this logic, but we must validate
2682 // the selector here because we implement it here, and we must implement it here because the AppKit
2683 // code checks the first responder.
2684 if (action == @selector(toggleSmartInsertDelete:)) {
2685 NSMenuItem *menuItem = (NSMenuItem *)item;
2686 if ([menuItem isKindOfClass:[NSMenuItem class]])
2687 [menuItem setState:[self smartInsertDeleteEnabled] ? NSOnState : NSOffState];
2688 return [self _canEdit];
2690 if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
2691 NSMenuItem *menuItem = (NSMenuItem *)item;
2692 if ([menuItem isKindOfClass:[NSMenuItem class]])
2693 [menuItem setState:[self isAutomaticQuoteSubstitutionEnabled] ? NSOnState : NSOffState];
2694 return [self _canEdit];
2696 if (action == @selector(toggleAutomaticLinkDetection:)) {
2697 NSMenuItem *menuItem = (NSMenuItem *)item;
2698 if ([menuItem isKindOfClass:[NSMenuItem class]])
2699 [menuItem setState:[self isAutomaticLinkDetectionEnabled] ? NSOnState : NSOffState];
2700 return [self _canEdit];
2702 if (action == @selector(toggleAutomaticDashSubstitution:)) {
2703 NSMenuItem *menuItem = (NSMenuItem *)item;
2704 if ([menuItem isKindOfClass:[NSMenuItem class]])
2705 [menuItem setState:[self isAutomaticDashSubstitutionEnabled] ? NSOnState : NSOffState];
2706 return [self _canEdit];
2708 if (action == @selector(toggleAutomaticTextReplacement:)) {
2709 NSMenuItem *menuItem = (NSMenuItem *)item;
2710 if ([menuItem isKindOfClass:[NSMenuItem class]])
2711 [menuItem setState:[self isAutomaticTextReplacementEnabled] ? NSOnState : NSOffState];
2712 return [self _canEdit];
2714 if (action == @selector(toggleAutomaticSpellingCorrection:)) {
2715 NSMenuItem *menuItem = (NSMenuItem *)item;
2716 if ([menuItem isKindOfClass:[NSMenuItem class]])
2717 [menuItem setState:[self isAutomaticSpellingCorrectionEnabled] ? NSOnState : NSOffState];
2718 return [self _canEdit];
2722 Editor::Command command = [self coreCommandBySelector:action];
2723 if (command.isSupported()) {
2724 NSMenuItem *menuItem = (NSMenuItem *)item;
2725 if ([menuItem isKindOfClass:[NSMenuItem class]])
2726 [menuItem setState:kit(command.state())];
2727 return command.isEnabled();
2733 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2735 // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
2736 // assumes the WebVIew is non-nil.
2737 if (![self _webView])
2739 BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2740 return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2743 - (BOOL)acceptsFirstResponder
2745 // Don't accept first responder when we first click on this view.
2746 // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2747 // Do accept first responder at any other time, for example from keyboard events,
2748 // or from calls back from WebCore once we begin mouse-down event handling.
2749 NSEvent *event = [NSApp currentEvent];
2750 if ([event type] == NSLeftMouseDown
2751 && !_private->handlingMouseDownEvent
2752 && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2758 - (BOOL)maintainsInactiveSelection
2760 // This method helps to determine whether the WebHTMLView should maintain
2761 // an inactive selection when it's not first responder.
2762 // Traditionally, these views have not maintained such selections,
2763 // clearing them when the view was not first responder. However,
2764 // to fix bugs like this one:
2765 // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even
2766 // when they're not firstResponder"
2767 // it was decided to add a switch to act more like an NSTextView.
2769 if ([[self _webView] maintainsInactiveSelection])
2772 // Predict the case where we are losing first responder status only to
2773 // gain it back again. Want to keep the selection in that case.
2774 id nextResponder = [[self window] _newFirstResponderAfterResigning];
2775 if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2776 id contentView = [nextResponder contentView];
2778 nextResponder = contentView;
2780 if ([nextResponder isKindOfClass:[NSClipView class]]) {
2781 id documentView = [nextResponder documentView];
2783 nextResponder = documentView;
2785 if (nextResponder == self)
2788 Frame* coreFrame = core([self _frame]);
2789 bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable();
2790 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2791 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2793 return selectionIsEditable && nextResponderIsInWebView;
2796 - (void)addMouseMovedObserver
2798 if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications)
2801 // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2802 if (!([[self window] isKeyWindow]
2803 #if ENABLE(DASHBOARD_SUPPORT)
2804 || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]
2809 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2810 name:WKMouseMovedNotification() object:nil];
2811 [self _frameOrBoundsChanged];
2812 _private->observingMouseMovedNotifications = true;
2815 - (void)removeMouseMovedObserver
2817 #if ENABLE(DASHBOARD_SUPPORT)
2818 // Don't remove the observer if we're running the Dashboard.
2819 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2823 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2824 [self _removeMouseMovedObserverUnconditionally];
2827 - (void)addSuperviewObservers
2829 if (_private->observingSuperviewNotifications)
2832 NSView *superview = [self superview];
2833 if (!superview || ![self window])
2836 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2837 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview];
2838 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview];
2840 // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2841 // It will check the current scroll against the previous layout's scroll. We need to
2842 // do this here to catch the case where the WebView is laid out at one size, removed from its
2843 // window, resized, and inserted into another window. Our frame/bounds changed notifications
2844 // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2845 [self _frameOrBoundsChanged];
2847 _private->observingSuperviewNotifications = true;
2850 - (void)addWindowObservers
2852 if (_private->observingWindowNotifications)
2855 NSWindow *window = [self window];
2859 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2860 [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
2861 [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
2862 [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
2864 _private->observingWindowNotifications = true;
2867 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2869 [self _removeSuperviewObservers];
2872 - (void)viewDidMoveToSuperview
2874 if ([self superview] != nil)
2875 [self addSuperviewObservers];
2877 #if USE(ACCELERATED_COMPOSITING)
2878 if ([self superview] && [self _isUsingAcceleratedCompositing]) {
2879 WebView *webView = [self _webView];
2880 if ([webView _postsAcceleratedCompositingNotifications])
2881 [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:webView userInfo:nil];
2886 - (void)viewWillMoveToWindow:(NSWindow *)window
2888 // Don't do anything if we aren't initialized. This happens
2889 // when decoding a WebView. When WebViews are decoded their subviews
2890 // are created by initWithCoder: and so won't be normally
2891 // initialized. The stub views are discarded by WebView.
2895 // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2896 [self _removeMouseMovedObserverUnconditionally];
2897 [self _removeWindowObservers];
2898 [self _removeSuperviewObservers];
2899 [self _cancelUpdateMouseoverTimer];
2901 // FIXME: This accomplishes the same thing as the call to setCanStartMedia(false) in
2902 // WebView. It would be nice to have a single mechanism instead of two.
2903 [[self _pluginController] stopAllPlugins];
2906 - (void)viewDidMoveToWindow
2908 // Don't do anything if we aren't initialized. This happens
2909 // when decoding a WebView. When WebViews are decoded their subviews
2910 // are created by initWithCoder: and so won't be normally
2911 // initialized. The stub views are discarded by WebView.
2912 if (!_private || _private->closed)
2915 [self _stopAutoscrollTimer];
2916 if ([self window]) {
2917 _private->lastScrollPosition = [[self superview] bounds].origin;
2918 [self addWindowObservers];
2919 [self addSuperviewObservers];
2920 [self addMouseMovedObserver];
2922 // FIXME: This accomplishes the same thing as the call to setCanStartMedia(true) in
2923 // WebView. It would be nice to have a single mechanism instead of two.
2924 [[self _pluginController] startAllPlugins];
2926 _private->lastScrollPosition = NSZeroPoint;
2930 - (void)_web_makePluginSubviewsPerformSelector:(SEL)selector withObject:(id)object
2932 #if ENABLE(NETSCAPE_PLUGIN_API)
2933 // Copy subviews because [self subviews] returns the view's mutable internal array,
2934 // and we must avoid mutating the array while enumerating it.
2935 NSArray *subviews = [[self subviews] copy];
2937 NSEnumerator *enumerator = [subviews objectEnumerator];
2938 WebNetscapePluginView *view;
2939 while ((view = [enumerator nextObject]) != nil)
2940 if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
2941 [view performSelector:selector withObject:object];
2947 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2949 [self _web_makePluginSubviewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2952 - (void)viewDidMoveToHostWindow
2954 [self _web_makePluginSubviewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2958 - (void)addSubview:(NSView *)view
2960 [super addSubview:view];
2962 if ([WebPluginController isPlugInView:view])
2963 [[self _pluginController] addPlugin:view];
2966 - (void)willRemoveSubview:(NSView *)subview
2969 // Have to null-check _private, since this can be called via -dealloc when
2970 // cleaning up the the layerHostingView.
2971 if (_private && _private->enumeratingSubviews)
2972 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]));
2975 if ([WebPluginController isPlugInView:subview])
2976 [[self _pluginController] destroyPlugin:subview];
2978 [super willRemoveSubview:subview];
2981 - (void)reapplyStyles
2984 double start = CFAbsoluteTimeGetCurrent();
2987 if (Frame* coreFrame = core([self _frame]))
2988 coreFrame->document()->styleSelectorChanged(RecalcStyleImmediately);
2991 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2992 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2996 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2997 // minPageWidth==0 implies a non-printing layout
2998 - (void)layoutToMinimumPageWidth:(float)minPageLogicalWidth height:(float)minPageLogicalHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustingViewSize:(BOOL)adjustViewSize
3000 if (![self _needsLayout])
3004 double start = CFAbsoluteTimeGetCurrent();
3007 LOG(View, "%@ doing layout", self);
3009 Frame* coreFrame = core([self _frame]);
3013 if (FrameView* coreView = coreFrame->view()) {
3014 if (minPageLogicalWidth > 0.0) {
3015 FloatSize pageSize(minPageLogicalWidth, minPageLogicalHeight);
3016 if (coreFrame->document() && coreFrame->document()->renderView() && !coreFrame->document()->renderView()->style()->isHorizontalWritingMode())
3017 pageSize = FloatSize(minPageLogicalHeight, minPageLogicalWidth);
3018 coreView->forceLayoutForPagination(pageSize, maximumShrinkRatio, adjustViewSize ? AdjustViewSize : DoNotAdjustViewSize);
3020 coreView->forceLayout(!adjustViewSize);
3022 coreView->adjustViewSize();
3027 double thisTime = CFAbsoluteTimeGetCurrent() - start;
3028 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
3034 [self layoutToMinimumPageWidth:0 height:0 maximumShrinkRatio:0 adjustingViewSize:NO];
3037 // Deliver mouseup events to the DOM for button 2.
3038 - (void)rightMouseUp:(NSEvent *)event
3040 // There's a chance that if we run a nested event loop the event will be released.
3041 // Retaining and then autoreleasing prevents that from causing a problem later here or
3042 // inside AppKit code.
3043 [[event retain] autorelease];
3045 [super rightMouseUp:event];
3047 if (Frame* coreframe = core([self _frame]))
3048 coreframe->eventHandler()->mouseUp(event);
3051 static void setMenuItemTarget(NSMenuItem* menuItem)
3053 // Don't set the menu item's action to the context menu action forwarder if we already
3055 if ([menuItem action])
3058 [menuItem setTarget:[WebMenuTarget sharedMenuTarget]];
3059 [menuItem setAction:@selector(forwardContextMenuAction:)];
3062 static void setMenuTargets(NSMenu* menu)
3064 NSInteger itemCount = [menu numberOfItems];
3065 for (NSInteger i = 0; i < itemCount; ++i) {
3066 NSMenuItem *item = [menu itemAtIndex:i];
3067 setMenuItemTarget(item);
3068 if ([item hasSubmenu])
3069 setMenuTargets([item submenu]);
3073 - (NSMenu *)menuForEvent:(NSEvent *)event
3075 // There's a chance that if we run a nested event loop the event will be released.
3076 // Retaining and then autoreleasing prevents that from causing a problem later here or
3077 // inside AppKit code.
3078 [[event retain] autorelease];
3080 [_private->completionController endRevertingChange:NO moveLeft:NO];
3082 RefPtr<Frame> coreFrame = core([self _frame]);
3086 Page* page = coreFrame->page();
3090 // Match behavior of other browsers by sending a mousedown event for right clicks.
3091 _private->handlingMouseDownEvent = YES;
3092 page->contextMenuController()->clearContextMenu();
3093 coreFrame->eventHandler()->mouseDown(event);
3094 BOOL handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformMouseEvent(event, page->chrome()->platformPageClient()));
3095 _private->handlingMouseDownEvent = NO;
3100 // Re-get page, since it might have gone away during event handling.
3101 page = coreFrame->page();
3105 ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
3109 NSArray* menuItems = coreMenu->platformDescription();
3113 NSUInteger count = [menuItems count];
3117 NSMenu* menu = [[[NSMenu alloc] init] autorelease];
3118 for (NSUInteger i = 0; i < count; i++)
3119 [menu addItem:[menuItems objectAtIndex:i]];
3120 setMenuTargets(menu);
3122 [[WebMenuTarget sharedMenuTarget] setMenuController:page->contextMenuController()];
3127 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
3129 return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
3134 Frame* coreFrame = core([self _frame]);
3137 Document* document = coreFrame->document();
3141 document->setFocusedNode(0);
3146 return [[self _webView] drawsBackground];
3150 - (void)setNeedsDisplay:(BOOL)flag
3152 LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO");
3153 [super setNeedsDisplay:flag];
3157 - (void)setNeedsDisplayInRect:(NSRect)invalidRect
3159 if (_private->inScrollPositionChanged) {
3160 // When scrolling, the dirty regions are adjusted for the scroll only
3161 // after NSViewBoundsDidChangeNotification is sent. Translate the invalid
3162 // rect to pre-scrolled coordinates in order to get the right dirty region
3163 // after adjustment. See <rdar://problem/7678927>.
3164 NSPoint origin = [[self superview] bounds].origin;
3165 invalidRect.origin.x -= _private->lastScrollPosition.x - origin.x;
3166 invalidRect.origin.y -= _private->lastScrollPosition.y - origin.y;
3168 [super setNeedsDisplayInRect:invalidRect];
3171 - (void)setNeedsLayout: (BOOL)flag
3173 LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO");
3175 return; // There's no way to say you don't need a layout.
3176 if (Frame* frame = core([self _frame])) {
3177 if (frame->document() && frame->document()->inPageCache())
3179 if (FrameView* view = frame->view())
3180 view->setNeedsLayout();
3184 - (void)setNeedsToApplyStyles: (BOOL)flag
3186 LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO");
3188 return; // There's no way to say you don't need a style recalc.
3189 if (Frame* frame = core([self _frame])) {
3190 if (frame->document() && frame->document()->inPageCache())
3192 frame->document()->scheduleForcedStyleRecalc();
3196 - (void)drawSingleRect:(NSRect)rect
3198 [NSGraphicsContext saveGraphicsState];
3201 ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
3203 [(WebClipView *)[self superview] setAdditionalClip:rect];
3206 if ([self _transparentBackground]) {
3207 [[NSColor clearColor] set];
3211 [[self _frame] _drawRect:rect contentsOnly:YES];
3213 WebView *webView = [self _webView];
3215 // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
3216 // called after the WebView has closed. If the client did not properly close the WebView and set the
3217 // UIDelegate to nil, then the UIDelegate will be stale and this code will crash.
3218 static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
3219 if (version3OrLaterClient)
3220 [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
3222 if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight])
3223 [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]];
3225 [(WebClipView *)[self superview] resetAdditionalClip];
3227 [NSGraphicsContext restoreGraphicsState];
3228 } @catch (NSException *localException) {
3229 [(WebClipView *)[self superview] resetAdditionalClip];
3230 [NSGraphicsContext restoreGraphicsState];
3231 LOG_ERROR("Exception caught while drawing: %@", localException);
3232 [localException raise];
3236 - (void)drawRect:(NSRect)rect
3238 ASSERT_MAIN_THREAD();
3239 LOG(View, "%@ drawing", self);
3241 const NSRect *rects;
3243 [self getRectsBeingDrawn:&rects count:&count];
3245 BOOL subviewsWereSetAside = _private->subviewsSetAside;
3246 if (subviewsWereSetAside)
3247 [self _restoreSubviews];
3250 double start = CFAbsoluteTimeGetCurrent();
3253 WebView *webView = [self _webView];
3254 if ([webView _mustDrawUnionedRect:rect singleRects:rects count:count])
3255 [self drawSingleRect:rect];
3257 for (int i = 0; i < count; ++i)
3258 [self drawSingleRect:rects[i]];
3261 double thisTime = CFAbsoluteTimeGetCurrent() - start;
3262 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
3265 if (subviewsWereSetAside)
3266 [self _setAsideSubviews];
3268 #if USE(ACCELERATED_COMPOSITING)
3269 // Only do the synchronization dance if we're drawing into the window, otherwise
3270 // we risk disabling screen updates when no flush is pending.
3271 if ([NSGraphicsContext currentContext] == [[self window] graphicsContext] && [webView _needsOneShotDrawingSynchronization]) {
3272 // Disable screen updates to minimize the chances of the race between the CA
3273 // display link and AppKit drawing causing flashes.
3274 [[self window] disableScreenUpdatesUntilFlush];
3276 // Make sure any layer changes that happened as a result of layout
3277 // via -viewWillDraw are committed.
3278 [CATransaction flush];
3279 [webView _setNeedsOneShotDrawingSynchronization:NO];
3284 CallUIDelegate(webView, @selector(webView:didDrawFrame:), [self _frame]);
3287 // Turn off the additional clip while computing our visibleRect.
3288 - (NSRect)visibleRect
3290 if (!([[self superview] isKindOfClass:[WebClipView class]]))
3291 return [super visibleRect];
3293 WebClipView *clipView = (WebClipView *)[self superview];
3295 BOOL hasAdditionalClip = [clipView hasAdditionalClip];
3296 if (!hasAdditionalClip) {
3297 return [super visibleRect];
3300 NSRect additionalClip = [clipView additionalClip];
3301 [clipView resetAdditionalClip];
3302 NSRect visibleRect = [super visibleRect];
3303 [clipView setAdditionalClip:additionalClip];
3307 - (void)_invalidateGStatesForTree
3309 // AppKit is in the process of traversing the NSView tree, and is going to send -renewGState to
3310 // descendants, including plug-in views. This can result in calls out to plug-in code and back into
3311 // WebCore via JavaScript, which could normally mutate the NSView tree while it is being traversed.
3312 // Defer those mutations while descendants are being traveresed.
3313 RenderWidget::suspendWidgetHierarchyUpdates();
3314 [super _invalidateGStatesForTree];
3315 RenderWidget::resumeWidgetHierarchyUpdates();
3323 - (void)windowDidBecomeKey:(NSNotification *)notification
3325 if (!pthread_main_np()) {
3326 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3330 NSWindow *keyWindow = [notification object];
3332 if (keyWindow == [self window]) {
3333 [self addMouseMovedObserver];
3334 [self _updateSecureInputState];
3338 - (void)windowDidResignKey:(NSNotification *)notification
3340 if (!pthread_main_np()) {
3341 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3345 NSWindow *formerKeyWindow = [notification object];
3347 if (formerKeyWindow == [self window])
3348 [self removeMouseMovedObserver];
3350 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
3351 [self _updateSecureInputState];
3352 [_private->completionController endRevertingChange:NO moveLeft:NO];
3356 - (void)windowWillClose:(NSNotification *)notification
3358 if (!pthread_main_np()) {
3359 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3363 [_private->completionController endRevertingChange:NO moveLeft:NO];
3364 [[self _pluginController] destroyAllPlugins];
3367 - (void)scrollWheel:(NSEvent *)event
3369 // There's a chance that responding to this event will run a nested event loop, and
3370 // fetching a new event might release the old one. Retaining and then autoreleasing
3371 // the current event prevents that from causing a problem inside WebKit or AppKit code.
3372 [[event retain] autorelease];
3374 Frame* frame = core([self _frame]);
3375 if (!frame || !frame->eventHandler()->wheelEvent(event))
3376 [super scrollWheel:event];
3379 - (BOOL)_isSelectionEvent:(NSEvent *)event
3381 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3382 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
3385 - (BOOL)_isScrollBarEvent:(NSEvent *)event
3387 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3388 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsInScrollBarKey] boolValue];
3391 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3393 // There's a chance that responding to this event will run a nested event loop, and
3394 // fetching a new event might release the old one. Retaining and then autoreleasing
3395 // the current event prevents that from causing a problem inside WebKit or AppKit code.
3396 [[event retain] autorelease];
3398 NSView *hitView = [self _hitViewForEvent:event];
3399 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3401 #if ENABLE(DASHBOARD_SUPPORT)
3402 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
3407 bool result = false;
3408 if (Frame* coreFrame = core([hitHTMLView _frame])) {
3409 coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
3410 [hitHTMLView _setMouseDownEvent:event];
3411 if ([hitHTMLView _isSelectionEvent:event]) {
3412 if (Page* page = coreFrame->page())
3413 result = coreFrame->eventHandler()->eventMayStartDrag(PlatformMouseEvent(event, page->chrome()->platformPageClient()));
3414 } else if ([hitHTMLView _isScrollBarEvent:event])
3416 [hitHTMLView _setMouseDownEvent:nil];
3420 return [hitView acceptsFirstMouse:event];
3423 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3425 // There's a chance that responding to this event will run a nested event loop, and
3426 // fetching a new event might release the old one. Retaining and then autoreleasing
3427 // the current event prevents that from causing a problem inside WebKit or AppKit code.
3428 [[event retain] autorelease];
3430 NSView *hitView = [self _hitViewForEvent:event];
3431 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3433 bool result = false;
3434 if ([hitHTMLView _isSelectionEvent:event]) {
3435 if (Frame* coreFrame = core([hitHTMLView _frame])) {
3436 [hitHTMLView _setMouseDownEvent:event];
3437 if (Page* page = coreFrame->page())
3438 result = coreFrame->eventHandler()->eventMayStartDrag(PlatformMouseEvent(event, page->chrome()->platformPageClient()));
3439 [hitHTMLView _setMouseDownEvent:nil];
3444 return [hitView shouldDelayWindowOrderingForEvent:event];
3447 - (void)mouseDown:(NSEvent *)event
3449 // There's a chance that responding to this event will run a nested event loop, and
3450 // fetching a new event might release the old one. Retaining and then autoreleasing
3451 // the current event prevents that from causing a problem inside WebKit or AppKit code.
3452 [[event retain] autorelease];
3454 RetainPtr<WebHTMLView> protector = self;
3455 if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
3458 _private->handlingMouseDownEvent = YES;
3460 // Record the mouse down position so we can determine drag hysteresis.
3461 [self _setMouseDownEvent:event];
3463 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3464 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3467 [_private->completionController endRevertingChange:NO moveLeft:NO];
3469 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
3470 // We don't want to pass them along to KHTML a second time.
3471 if (!([event modifierFlags] & NSControlKeyMask)) {
3472 _private->ignoringMouseDraggedEvents = NO;
3474 // Don't do any mouseover while the mouse is down.
3475 [self _cancelUpdateMouseoverTimer];
3477 // Let WebCore get a chance to deal with the event. This will call back to us
3478 // to start the autoscroll timer if appropriate.
3479 if (Frame* coreframe = core([self _frame]))
3480 coreframe->eventHandler()->mouseDown(event);
3484 _private->handlingMouseDownEvent = NO;
3487 - (void)dragImage:(NSImage *)dragImage
3489 offset:(NSSize)offset
3490 event:(NSEvent *)event
3491 pasteboard:(NSPasteboard *)pasteboard
3493 slideBack:(BOOL)slideBack
3495 ASSERT(self == [self _topHTMLView]);
3496 [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
3499 - (void)mouseDragged:(NSEvent *)event
3501 // There's a chance that responding to this event will run a nested event loop, and
3502 // fetching a new event might release the old one. Retaining and then autoreleasing
3503 // the current event prevents that from causing a problem inside WebKit or AppKit code.
3504 [[event retain] autorelease];
3506 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3507 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3512 if (!_private->ignoringMouseDraggedEvents) {
3513 if (Frame* frame = core([self _frame])) {
3514 if (Page* page = frame->page())
3515 page->mainFrame()->eventHandler()->mouseDragged(event);
3522 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
3524 ASSERT(![self _webView] || [self _isTopHTMLView]);
3526 Page* page = core([self _webView]);
3528 return NSDragOperationNone;
3530 return (NSDragOperation)page->dragController()->sourceDragOperation();
3533 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
3535 ASSERT(![self _webView] || [self _isTopHTMLView]);
3537 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
3538 NSPoint windowMouseLoc = windowImageLoc;
3540 if (Page* page = core([self _webView])) {
3541 DragController* dragController = page->dragController();
3542 windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3543 dragController->dragEnded();
3546 [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation];
3548 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3549 _private->ignoringMouseDraggedEvents = YES;
3551 // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
3552 // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
3553 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
3554 location:windowMouseLoc
3555 modifierFlags:[[NSApp currentEvent] modifierFlags]
3556 timestamp:[NSDate timeIntervalSinceReferenceDate]
3557 windowNumber:[[self window] windowNumber]
3558 context:[[NSApp currentEvent] context]
3559 eventNumber:0 clickCount:0 pressure:0];
3560 [self mouseUp:fakeEvent]; // This will also update the mouseover state.
3563 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3565 NSFileWrapper *wrapper = nil;
3566 NSURL *draggingImageURL = nil;
3568 if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) {
3570 SharedBuffer *buffer = static_cast<CachedResource*>(tiffResource)->data();
3572 goto noPromisedData;
3574 NSData *data = buffer->createNSData();
3575 NSURLResponse *response = tiffResource->response().nsURLResponse();
3576 draggingImageURL = [response URL];
3577 wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
3578 NSString* filename = [response suggestedFilename];
3579 NSString* trueExtension(tiffResource->image()->filenameExtension());
3580 if (!matchesExtensionOrEquivalent(filename, trueExtension))
3581 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension];
3582 [wrapper setPreferredFilename:filename];
3588 ASSERT(![self _webView] || [self _isTopHTMLView]);
3589 Page* page = core([self _webView]);
3591 //If a load occurs midway through a drag, the view may be detached, which gives
3592 //us no ability to get to the original Page, so we cannot access any drag state
3593 //FIXME: is there a way to recover?
3597 const KURL& imageURL = page->dragController()->draggingImageURL();
3598 ASSERT(!imageURL.isEmpty());
3599 draggingImageURL = imageURL;
3601 wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL];
3604 if (wrapper == nil) {
3605 LOG_ERROR("Failed to create image file.");
3609 // FIXME: Report an error if we fail to create a file.
3610 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3611 path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
3612 if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
3613 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
3615 if (draggingImageURL)
3616 [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path];
3618 return [NSArray arrayWithObject:[path lastPathComponent]];
3621 - (void)mouseUp:(NSEvent *)event
3623 // There's a chance that responding to this event will run a nested event loop, and
3624 // fetching a new event might release the old one. Retaining and then autoreleasing
3625 // the current event prevents that from causing a problem inside WebKit or AppKit code.
3626 [[event retain] autorelease];
3628 [self _setMouseDownEvent:nil];
3630 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3631 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3636 [self _stopAutoscrollTimer];
3637 if (Frame* frame = core([self _frame])) {
3638 if (Page* page = frame->page())
3639 page->mainFrame()->eventHandler()->mouseUp(event);
3641 [self _updateMouseoverWithFakeEvent];
3646 - (void)mouseMovedNotification:(NSNotification *)notification
3648 [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
3651 // returning YES from this method is the way we tell AppKit that it is ok for this view
3652 // to be in the key loop even when "tab to all controls" is not on.
3653 - (BOOL)needsPanelToBecomeKey
3658 // Utility function to make sure we don't return anything through the NSTextInput
3659 // API when an editable region is not currently focused.
3660 static BOOL isTextInput(Frame* coreFrame)
3662 return coreFrame && !coreFrame->selection()->isNone() && coreFrame->selection()->isContentEditable();
3665 static BOOL isInPasswordField(Frame* coreFrame)
3667 return coreFrame && coreFrame->selection()->isInPasswordField();
3670 static PassRefPtr<KeyboardEvent> currentKeyboardEvent(Frame* coreFrame)
3672 NSEvent *event = [NSApp currentEvent];
3676 switch ([event type]) {
3678 PlatformKeyboardEvent platformEvent(event);
3679 platformEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown);
3680 return KeyboardEvent::create(platformEvent, coreFrame->document()->defaultView());
3683 return KeyboardEvent::create(event, coreFrame->document()->defaultView());
3689 - (BOOL)becomeFirstResponder
3691 NSSelectionDirection direction = NSDirectSelection;
3692 if (![[self _webView] _isPerformingProgrammaticFocus])
3693 direction = [[self window] keyViewSelectionDirection];
3695 [self _updateFontPanel];
3697 Frame* frame = core([self _frame]);
3701 BOOL exposeInputContext = isTextInput(frame) && !isInPasswordField(frame);
3702 if (exposeInputContext != _private->exposeInputContext) {
3703 _private->exposeInputContext = exposeInputContext;
3704 [NSApp updateWindows];
3707 _private->_forceUpdateSecureInputState = YES;
3708 [self _updateSecureInputState];
3709 _private->_forceUpdateSecureInputState = NO;
3711 frame->editor()->setStartNewKillRingSequence(true);
3713 Page* page = frame->page();
3717 if (![[self _webView] _isPerformingProgrammaticFocus])
3718 page->focusController()->setFocusedFrame(frame);
3720 page->focusController()->setFocused(true);
3722 if (direction == NSDirectSelection)
3725 if (Document* document = frame->document())
3726 document->setFocusedNode(0);
3727 page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward,
3728 currentKeyboardEvent(frame).get());
3732 - (BOOL)resignFirstResponder
3734 BOOL resign = [super resignFirstResponder];
3736 if (_private->isInSecureInputState) {
3737 DisableSecureEventInput();
3738 _private->isInSecureInputState = NO;
3740 [_private->completionController endRevertingChange:NO moveLeft:NO];
3741 Frame* coreFrame = core([self _frame]);
3744 Page* page = coreFrame->page();
3747 if (![self maintainsInactiveSelection]) {
3749 if (![[self _webView] _isPerformingProgrammaticFocus])
3753 id nextResponder = [[self window] _newFirstResponderAfterResigning];
3754 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
3755 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
3756 if (!nextResponderIsInWebView)
3757 page->focusController()->setFocused(false);
3762 - (void)setDataSource:(WebDataSource *)dataSource
3765 if (_private->dataSource != dataSource) {
3766 ASSERT(!_private->closed);
3767 BOOL hadDataSource = _private->dataSource != nil;
3769 [dataSource retain];
3770 [_private->dataSource release];
3771 _private->dataSource = dataSource;
3772 [_private->pluginController setDataSource:dataSource];
3775 [self addMouseMovedObserver];
3779 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3783 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3784 // key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3785 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3786 - (void)updateCell:(NSCell*)cell
3790 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3791 // pageWidth != 0 implies we will relayout to a new width
3792 - (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageLogicalWidth logicalHeight:(float)minPageLogicalHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent
3794 if (printing == _private->printing && paginateScreenContent == _private->paginateScreenContent)
3797 WebFrame *frame = [self _frame];
3798 NSArray *subframes = [frame childFrames];
3799 unsigned n = [subframes count];
3801 for (i = 0; i != n; ++i) {
3802 WebFrame *subframe = [subframes objectAtIndex:i];
3803 WebFrameView *frameView = [subframe frameView];
3804 if ([[subframe _dataSource] _isDocumentHTML]) {
3805 [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:adjustViewSize paginateScreenContent:paginateScreenContent];
3809 [_private->pageRects release];
3810 _private->pageRects = nil;
3811 _private->printing = printing;
3812 _private->paginateScreenContent = paginateScreenContent;
3814 Frame* coreFrame = core([self _frame]);
3816 if (FrameView* coreView = coreFrame->view())
3817 coreView->setMediaType(_private->printing ? "print" : "screen");
3818 if (Document* document = coreFrame->document()) {
3819 // In setting printing, we should not validate resources already cached for the document.
3820 // See https://bugs.webkit.org/show_bug.cgi?id=43704
3821 ResourceCacheValidationSuppressor validationSuppressor(document->cachedResourceLoader());
3823 document->setPaginatedForScreen(_private->paginateScreenContent);
3824 document->setPrinting(_private->printing);
3825 document->styleSelectorChanged(RecalcStyleImmediately);
3829 [self setNeedsLayout:YES];
3830 [self layoutToMinimumPageWidth:minPageLogicalWidth height:minPageLogicalHeight maximumShrinkRatio:maximumShrinkRatio adjustingViewSize:adjustViewSize];
3832 // Can't do this when starting printing or nested printing won't work, see 3491427.
3833 [self setNeedsDisplay:NO];
3837 - (BOOL)canPrintHeadersAndFooters
3842 // This is needed for the case where the webview is embedded in the view that's being printed.
3843 // It shouldn't be called when the webview is being printed directly.
3844 - (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit
3846 // This helps when we print as part of a larger print process.
3847 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3848 BOOL wasInPrintingMode = _private->printing;
3849 if (!wasInPrintingMode)
3850 [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
3852 *newBottom = [self _adjustedBottomOfPageWithTop:oldTop bottom:oldBottom limit:bottomLimit];
3854 if (!wasInPrintingMode) {
3855 NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation];
3856 if (currenPrintOperation)
3857 // delay _setPrinting:NO until back to main loop as this method may get called repeatedly
3858 [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0];
3860 // not sure if this is actually ever invoked, it probably shouldn't be
3861 [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
3865 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3867 bool useViewWidth = true;
3868 Frame* coreFrame = core([self _frame]);
3870 Document* document = coreFrame->document();
3871 if (document && document->renderView())
3872 useViewWidth = document->renderView()->style()->isHorizontalWritingMode();
3875 float viewLogicalWidth = useViewWidth ? NSWidth([self bounds]) : NSHeight([self bounds]);
3876 if (viewLogicalWidth < 1) {
3877 LOG_ERROR("%@ has no logical width when printing", self);
3881 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3882 float maxShrinkToFitScaleFactor = 1.0f / _WebHTMLViewPrintingMaximumShrinkFactor;
3883 float shrinkToFitScaleFactor = (useViewWidth ? [printOperation _web_availablePaperWidth] : [printOperation _web_availablePaperHeight]) / viewLogicalWidth;
3884 return userScaleFactor * max(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor);
3887 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3888 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3889 // if AppKit makes it SPI/API.
3890 - (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3892 return [self _scaleFactorForPrintOperation:printOperation];
3895 // This is used for Carbon printing. At some point we might want to make this public API.
3896 - (void)setPageWidthForPrinting:(float)pageWidth
3898 [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
3899 [self _setPrinting:YES minimumPageLogicalWidth:pageWidth logicalHeight:0 maximumShrinkRatio:1 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
3902 - (void)_endPrintModeAndRestoreWindowAutodisplay
3904 [self _endPrintMode];
3905 [[self window] setAutodisplay:YES];
3908 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3910 ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3911 NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3912 if (initiatingOperation == currentOperation) {
3913 // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3914 // being extra paranoid here since the printing code is so fragile. Delay the cleanup
3916 ASSERT_NOT_REACHED();
3917 [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3918 } else if ([currentOperation view] == self) {
3919 // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3920 // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3921 // fragile. Do nothing, because we don't want to break the print job currently in progress, and
3922 // the print job currently in progress is responsible for its own cleanup.
3923 ASSERT_NOT_REACHED();
3925 // The print job that kicked off this delayed call has finished, and this view is not being
3926 // printed again. We expect that no other print job has started. Since this delayed call wasn't
3927 // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3928 // the print mode here.
3929 ASSERT(currentOperation == nil);
3930 [self _endPrintModeAndRestoreWindowAutodisplay];
3934 // Return the number of pages available for printing
3935 - (BOOL)knowsPageRange:(NSRangePointer)range
3937 // Must do this explicit display here, because otherwise the view might redisplay while the print
3938 // sheet was up, using printer fonts (and looking different).
3939 [self displayIfNeeded];
3940 [[self window] setAutodisplay:NO];
3942 [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3943 NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3944 if (![self _beginPrintModeWithPageWidth:[printOperation _web_availablePaperWidth] height:[printOperation _web_availablePaperHeight] shrinkToFit:YES])
3947 // Certain types of errors, including invalid page ranges, can cause beginDocument and
3948 // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3949 // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3950 // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3951 // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3952 // which is after beginDocument and endDocument would be called.
3953 [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3955 // There is a theoretical chance that someone could do some drawing between here and endDocument,
3956 // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3957 // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3959 range->location = 1;
3960 float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3961 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3962 [_private->pageRects release];
3963 float fullPageWidth = floorf([printOperation _web_availablePaperWidth] / totalScaleFactor);
3964 float fullPageHeight = floorf([printOperation _web_availablePaperHeight] / totalScaleFactor);
3965 WebFrame *frame = [self _frame];
3966 NSArray *newPageRects = [frame _computePageRectsWithPrintScaleFactor:userScaleFactor pageSize:NSMakeSize(fullPageWidth, fullPageHeight)];
3968 // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3969 // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3970 // a blank page (with correct-looking header and footer if that option is on), which matches
3971 // the behavior of IE and Camino at least.
3972 if ([newPageRects count] == 0)
3973 newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3975 _private->pageRects = [newPageRects retain];
3977 range->length = [_private->pageRects count];
3982 // Return the drawing rectangle for a particular page number
3983 - (NSRect)rectForPage:(NSInteger)page
3985 return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3988 - (void)drawPageBorderWithSize:(NSSize)borderSize
3990 ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));
3991 [[self _webView] _drawHeaderAndFooter];
3994 - (void)beginDocument
3997 // From now on we'll get a chance to call _endPrintMode in either beginDocument or
3998 // endDocument, so we can cancel the "just in case" pending call.
3999 [NSObject cancelPreviousPerformRequestsWithTarget:self
4000 selector:@selector(_delayedEndPrintMode:)
4001 object:[NSPrintOperation currentOperation]];
4002 [super beginDocument];
4003 } @catch (NSException *localException) {
4004 // Exception during [super beginDocument] means that endDocument will not get called,
4005 // so we need to clean up our "print mode" here.
4006 [self _endPrintModeAndRestoreWindowAutodisplay];
4012 [super endDocument];
4013 // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO
4014 [self _endPrintModeAndRestoreWindowAutodisplay];
4017 - (void)keyDown:(NSEvent *)event
4019 // There's a chance that responding to this event will run a nested event loop, and
4020 // fetching a new event might release the old one. Retaining and then autoreleasing
4021 // the current event prevents that from causing a problem inside WebKit or AppKit code.
4022 [[event retain] autorelease];
4024 RetainPtr<WebHTMLView> selfProtector = self;
4025 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
4027 BOOL callSuper = NO;
4029 [_private->keyDownEvent release];
4030 _private->keyDownEvent = [event retain];
4032 BOOL completionPopupWasOpen = _private->completionController && [_private->completionController popupWindowIsOpen];
4033 Frame* coreFrame = core([self _frame]);
4034 if (!eventWasSentToWebCore && coreFrame && coreFrame->eventHandler()->keyEvent(event)) {
4035 // WebCore processed a key event, bail on any preexisting complete: UI
4036 if (completionPopupWasOpen)
4037 [_private->completionController endRevertingChange:YES moveLeft:NO];
4038 } else if (!_private->completionController || ![_private->completionController filterKeyDown:event]) {
4039 // Not consumed by complete: popup window
4040 [_private->completionController endRevertingChange:YES moveLeft:NO];
4044 [super keyDown:event];
4046 [NSCursor setHiddenUntilMouseMoves:YES];
4049 - (void)keyUp:(NSEvent *)event
4051 // There's a chance that responding to this event will run a nested event loop, and
4052 // fetching a new event might release the old one. Retaining and then autoreleasing
4053 // the current event prevents that from causing a problem inside WebKit or AppKit code.
4054 [[event retain] autorelease];
4056 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
4058 RetainPtr<WebHTMLView> selfProtector = self;
4059 Frame* coreFrame = core([self _frame]);
4060 if (coreFrame && !eventWasSentToWebCore)
4061 coreFrame->eventHandler()->keyEvent(event);
4063 [super keyUp:event];
4066 - (void)flagsChanged:(NSEvent *)event
4068 // There's a chance that responding to this event will run a nested event loop, and
4069 // fetching a new event might release the old one. Retaining and then autoreleasing
4070 // the current event prevents that from causing a problem inside WebKit or AppKit code.
4071 [[event retain] autorelease];
4073 RetainPtr<WebHTMLView> selfProtector = self;
4075 Frame* coreFrame = core([self _frame]);
4076 unsigned short keyCode = [event keyCode];
4078 // Don't make an event from the num lock and function keys.
4079 if (coreFrame && keyCode != 0 && keyCode != 10 && keyCode != 63) {
4080 coreFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(event));
4084 [super flagsChanged:event];
4087 - (id)accessibilityAttributeValue:(NSString*)attributeName
4089 if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
4090 id accTree = [[self _frame] accessibilityRoot];
4092 return [NSArray arrayWithObject:accTree];
4095 return [super accessibilityAttributeValue:attributeName];
4098 - (id)accessibilityFocusedUIElement
4100 id accTree = [[self _frame] accessibilityRoot];
4102 return [accTree accessibilityFocusedUIElement];
4106 - (id)accessibilityHitTest:(NSPoint)point
4108 id accTree = [[self _frame] accessibilityRoot];
4110 NSPoint windowCoord = [[self window] convertScreenToBase:point];
4111 return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
4116 - (id)_accessibilityParentForSubview:(NSView *)subview
4118 id accTree = [[self _frame] accessibilityRoot];
4121 id parent = [accTree _accessibilityParentForSubview:subview];
4127 - (void)centerSelectionInVisibleArea:(id)sender
4131 if (Frame* coreFrame = core([self _frame]))
4132 coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways);
4135 - (NSData *)_selectionStartFontAttributesAsRTF
4137 Frame* coreFrame = core([self _frame]);
4138 NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x"
4139 attributes:coreFrame ? coreFrame->editor()->fontAttributesForSelectionStart() : nil];
4140 NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
4145 - (NSDictionary *)_fontAttributesFromFontPasteboard
4147 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
4148 if (fontPasteboard == nil)
4150 NSData *data = [fontPasteboard dataForType:NSFontPboardType];
4151 if (data == nil || [data length] == 0)
4153 // NSTextView does something more efficient by parsing the attributes only, but that's not available in API.
4154 NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease];
4155 if (string == nil || [string length] == 0)
4157 return [string fontAttributesInRange:NSMakeRange(0, 1)];
4160 - (DOMCSSStyleDeclaration *)_emptyStyle
4162 return [[[self _frame] DOMDocument] createCSSStyleDeclaration];
4165 - (NSString *)_colorAsString:(NSColor *)color
4167 NSColor *rgbColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
4168 // FIXME: If color is non-nil and rgbColor is nil, that means we got some kind
4169 // of fancy color that can't be converted to RGB. Changing that to "transparent"
4170 // might not be great, but it's probably OK.
4171 if (rgbColor == nil)
4172 return @"transparent";
4173 float r = [rgbColor redComponent];
4174 float g = [rgbColor greenComponent];
4175 float b = [rgbColor blueComponent];
4176 float a = [rgbColor alphaComponent];
4178 return @"transparent";
4179 if (r == 0 && g == 0 && b == 0 && a == 1)
4181 if (r == 1 && g == 1 && b == 1 && a == 1)
4183 // FIXME: Lots more named colors. Maybe we could use the table in WebCore?
4185 return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255];
4186 return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a];
4189 - (NSString *)_shadowAsString:(NSShadow *)shadow
4193 NSSize offset = [shadow shadowOffset];
4194 float blurRadius = [shadow shadowBlurRadius];
4195 if (offset.width == 0 && offset.height == 0 && blurRadius == 0)
4197 NSColor *color = [shadow shadowColor];
4200 // FIXME: Handle non-integral values here?
4201 if (blurRadius == 0)
4202 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height];
4203 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius];
4206 - (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary
4208 DOMCSSStyleDeclaration *style = [self _emptyStyle];
4210 NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName];
4211 [style setBackgroundColor:[self _colorAsString:color]];
4213 NSFont *font = [dictionary objectForKey:NSFontAttributeName];
4215 [style setFontFamily:@"Helvetica"];
4216 [style setFontSize:@"12px"];
4217 [style setFontWeight:@"normal"];
4218 [style setFontStyle:@"normal"];
4220 NSFontManager *fm = [NSFontManager sharedFontManager];
4221 // FIXME: Need more sophisticated escaping code if we want to handle family names
4222 // with characters like single quote or backslash in their names.
4223 [style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]];
4224 [style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]];
4225 // FIXME: Map to the entire range of CSS weight values.
4226 if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT)
4227 [style setFontWeight:@"bold"];
4229 [style setFontWeight:@"normal"];
4230 if ([fm traitsOfFont:font] & NSItalicFontMask)
4231 [style setFontStyle:@"italic"];
4233 [style setFontStyle:@"normal"];
4236 color = [dictionary objectForKey:NSForegroundColorAttributeName];
4237 [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"];
4239 NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName];
4240 [style setTextShadow:[self _shadowAsString:shadow]];
4242 int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue];
4244 int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue];
4245 if (superscriptInt > 0)
4246 [style setVerticalAlign:@"super"];
4247 else if (superscriptInt < 0)
4248 [style setVerticalAlign:@"sub"];
4250 [style setVerticalAlign:@"baseline"];
4251 int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue];
4252 // FIXME: Underline wins here if we have both (see bug 3790443).
4253 if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone)
4254 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
4255 else if (underlineInt == NSUnderlineStyleNone)
4256 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
4258 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
4263 - (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
4265 if (Frame* coreFrame = core([self _frame]))
4266 coreFrame->editor()->applyStyleToSelection(core(style), undoAction);
4269 - (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
4271 if (Frame* coreFrame = core([self _frame]))
4272 coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction);
4275 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
4277 WebView *webView = [self _webView];
4281 if (![[webView preferences] respectStandardStyleKeyEquivalents])
4284 if (![self _canEdit])
4287 if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
4290 NSString *string = [event characters];
4291 if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
4292 [self executeCoreCommandByName:"ToggleBold"];
4295 if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
4296 [self executeCoreCommandByName:"ToggleItalic"];
4303 - (BOOL)performKeyEquivalent:(NSEvent *)event
4305 // There's a chance that responding to this event will run a nested event loop, and
4306 // fetching a new event might release the old one. Retaining and then autoreleasing
4307 // the current event prevents that from causing a problem inside WebKit or AppKit code.
4308 [[event retain] autorelease];
4310 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
4313 [_private->keyDownEvent release];
4314 _private->keyDownEvent = [event retain];
4318 // Pass command-key combos through WebCore if there is a key binding available for
4319 // this event. This lets web pages have a crack at intercepting command-modified keypresses.
4320 // But don't do it if we have already handled the event.
4321 // Pressing Esc results in a fake event being sent - don't pass it to WebCore.
4322 if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder])
4323 if (Frame* frame = core([self _frame]))
4324 ret = frame->eventHandler()->keyEvent(event);
4327 ret = [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event];
4334 - (void)copyFont:(id)sender
4338 // Put RTF with font attributes on the pasteboard.
4339 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
4340 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
4341 [fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil];
4342 [fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType];
4345 - (void)pasteFont:(id)sender
4349 // Read RTF with font attributes from the pasteboard.
4350 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
4351 [self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:EditActionPasteFont];
4354 - (void)pasteAsRichText:(id)sender
4358 // Since rich text always beats plain text when both are on the pasteboard, it's not
4359 // clear how this is different from plain old paste.
4360 [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO];
4363 - (NSFont *)_originalFontA
4365 return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f];
4368 - (NSFont *)_originalFontB
4370 return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:NSFontItalicTrait weight:STANDARD_BOLD_WEIGHT size:12.0f];
4373 - (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b
4375 // Since there's no way to directly ask NSFontManager what style change it's going to do
4376 // we instead pass two "specimen" fonts to it and let it change them. We then deduce what
4377 // style change it was doing by looking at what happened to each of the two fonts.
4378 // So if it was making the text bold, both fonts will be bold after the fact.
4380 if (a == nil || b == nil)
4383 NSFontManager *fm = [NSFontManager sharedFontManager];
4385 NSFont *oa = [self _originalFontA];
4387 NSString *aFamilyName = [a familyName];
4388 NSString *bFamilyName = [b familyName];
4390 int aPointSize = (int)[a pointSize];
4391 int bPointSize = (int)[b pointSize];
4393 int aWeight = [fm weightOfFont:a];
4394 int bWeight = [fm weightOfFont:b];
4396 BOOL aIsItalic = ([fm traitsOfFont:a] & NSItalicFontMask) != 0;
4397 BOOL bIsItalic = ([fm traitsOfFont:b] & NSItalicFontMask) != 0;
4399 BOOL aIsBold = aWeight > MIN_BOLD_WEIGHT;
4401 if ([aFamilyName isEqualToString:bFamilyName]) {
4402 NSString *familyNameForCSS = aFamilyName;
4404 // The family name may not be specific enough to get us the font specified.
4405 // In some cases, the only way to get exactly what we are looking for is to use
4406 // the Postscript name.
4408 // Find the font the same way the rendering code would later if it encountered this CSS.
4409 NSFontTraitMask traits = aIsItalic ? NSFontItalicTrait : 0;
4410 int weight = aIsBold ? STANDARD_BOLD_WEIGHT : STANDARD_WEIGHT;
4411 NSFont *foundFont = [WebFontCache fontWithFamily:aFamilyName traits:traits weight:weight size:aPointSize];
4413 // If we don't find a font with the same Postscript name, then we'll have to use the
4414 // Postscript name to make the CSS specific enough.
4415 if (![[foundFont fontName] isEqualToString:[a fontName]])
4416 familyNameForCSS = [a fontName];
4418 // FIXME: Need more sophisticated escaping code if we want to handle family names
4419 // with characters like single quote or backslash in their names.
4420 [style setFontFamily:[NSString stringWithFormat:@"'%@'", familyNameForCSS]];
4423 int soa = (int)[oa pointSize];
4424 if (aPointSize == bPointSize)
4425 [style setFontSize:[NSString stringWithFormat:@"%dpx", aPointSize]];
4426 else if (aPointSize < soa)
4427 [style _setFontSizeDelta:@"-1px"];
4428 else if (aPointSize > soa)
4429 [style _setFontSizeDelta:@"1px"];
4431 // FIXME: Map to the entire range of CSS weight values.
4432 if (aWeight == bWeight)
4433 [style setFontWeight:aIsBold ? @"bold" : @"normal"];
4435 if (aIsItalic == bIsItalic)
4436 [style setFontStyle:aIsItalic ? @"italic" : @"normal"];
4439 - (DOMCSSStyleDeclaration *)_styleFromFontManagerOperation
4441 DOMCSSStyleDeclaration *style = [self _emptyStyle];
4443 NSFontManager *fm = [NSFontManager sharedFontManager];
4445 NSFont *oa = [self _originalFontA];
4446 NSFont *ob = [self _originalFontB];
4447 [self _addToStyle:style fontA:[fm convertFont:oa] fontB:[fm convertFont:ob]];
4452 - (void)changeFont:(id)sender
4456 [self _applyStyleToSelection:[self _styleFromFontManagerOperation] withUndoAction:EditActionSetFont];
4459 - (DOMCSSStyleDeclaration *)_styleForAttributeChange:(id)sender
4461 DOMCSSStyleDeclaration *style = [self _emptyStyle];
4463 NSShadow *shadow = [[NSShadow alloc] init];
4464 [shadow setShadowOffset:NSMakeSize(1, 1)];
4466 NSDictionary *oa = [NSDictionary dictionaryWithObjectsAndKeys:
4467 [self _originalFontA], NSFontAttributeName,
4469 NSDictionary *ob = [NSDictionary dictionaryWithObjectsAndKeys:
4470 [NSColor blackColor], NSBackgroundColorAttributeName,
4471 [self _originalFontB], NSFontAttributeName,
4472 [NSColor whiteColor], NSForegroundColorAttributeName,
4473 shadow, NSShadowAttributeName,
4474 [NSNumber numberWithInt:NSUnderlineStyleSingle], NSStrikethroughStyleAttributeName,
4475 [NSNumber numberWithInt:1], NSSuperscriptAttributeName,
4476 [NSNumber numberWithInt:NSUnderlineStyleSingle], NSUnderlineStyleAttributeName,
4483 NSObliquenessAttributeName /* float; skew to be applied to glyphs, default 0: no skew */
4484 // font-style, but that is just an on-off switch
4486 NSExpansionAttributeName /* float; log of expansion factor to be applied to glyphs, default 0: no expansion */
4489 NSKernAttributeName /* float, amount to modify default kerning, if 0, kerning off */
4490 // letter-spacing? probably not good enough
4492 NSUnderlineColorAttributeName /* NSColor, default nil: same as foreground color */
4493 NSStrikethroughColorAttributeName /* NSColor, default nil: same as foreground color */
4494 // text-decoration-color?
4496 NSLigatureAttributeName /* int, default 1: default ligatures, 0: no ligatures, 2: all ligatures */
4497 NSBaselineOffsetAttributeName /* float, in points; offset from baseline, default 0 */
4498 NSStrokeWidthAttributeName /* float, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0) */
4499 NSStrokeColorAttributeName /* NSColor, default nil: same as foreground color */
4504 NSDictionary *a = [sender convertAttributes:oa];
4505 NSDictionary *b = [sender convertAttributes:ob];
4507 NSColor *ca = [a objectForKey:NSBackgroundColorAttributeName];
4508 NSColor *cb = [b objectForKey:NSBackgroundColorAttributeName];
4510 [style setBackgroundColor:[self _colorAsString:ca]];
4513 [self _addToStyle:style fontA:[a objectForKey:NSFontAttributeName] fontB:[b objectForKey:NSFontAttributeName]];
4515 ca = [a objectForKey:NSForegroundColorAttributeName];
4516 cb = [b objectForKey:NSForegroundColorAttributeName];
4518 [style setColor:[self _colorAsString:ca]];
4521 NSShadow *sha = [a objectForKey:NSShadowAttributeName];
4523 [style setTextShadow:[self _shadowAsString:sha]];
4524 else if ([b objectForKey:NSShadowAttributeName] == nil)
4525 [style setTextShadow:@"none"];
4527 int sa = [[a objectForKey:NSStrikethroughStyleAttributeName] intValue];
4528 int sb = [[b objectForKey:NSStrikethroughStyleAttributeName] intValue];
4530 if (sa == NSUnderlineStyleNone)
4531 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
4532 // we really mean "no line-through" rather than "none"
4534 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
4535 // we really mean "add line-through" rather than "line-through"
4538 sa = [[a objectForKey:NSSuperscriptAttributeName] intValue];
4539 sb = [[b objectForKey:NSSuperscriptAttributeName] intValue];
4542 [style setVerticalAlign:@"super"];
4544 [style setVerticalAlign:@"sub"];
4546 [style setVerticalAlign:@"baseline"];
4549 int ua = [[a objectForKey:NSUnderlineStyleAttributeName] intValue];
4550 int ub = [[b objectForKey:NSUnderlineStyleAttributeName] intValue];
4552 if (ua == NSUnderlineStyleNone)
4553 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
4554 // we really mean "no underline" rather than "none"
4556 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
4557 // we really mean "add underline" rather than "underline"
4563 - (void)changeAttributes:(id)sender
4567 [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes];
4570 - (DOMCSSStyleDeclaration *)_styleFromColorPanelWithSelector:(SEL)selector
4572 DOMCSSStyleDeclaration *style = [self _emptyStyle];
4574 ASSERT([style respondsToSelector:selector]);
4575 [style performSelector:selector withObject:[self _colorAsString:[[NSColorPanel sharedColorPanel] color]]];
4580 - (EditAction)_undoActionFromColorPanelWithSelector:(SEL)selector
4582 if (selector == @selector(setBackgroundColor:))
4583 return EditActionSetBackgroundColor;
4584 return EditActionSetColor;
4587 - (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range
4589 DOMCSSStyleDeclaration *style = [self _styleFromColorPanelWithSelector:selector];
4590 WebView *webView = [self _webView];
4591 if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:range])
4592 if (Frame* coreFrame = core([self _frame]))
4593 coreFrame->editor()->applyStyle(core(style), [self _undoActionFromColorPanelWithSelector:selector]);
4596 - (void)changeDocumentBackgroundColor:(id)sender
4600 // Mimicking NSTextView, this method sets the background color for the
4601 // entire document. There is no NSTextView API for setting the background
4602 // color on the selected range only. Note that this method is currently
4603 // never called from the UI (see comment in changeColor:).
4604 // FIXME: this actually has no effect when called, probably due to 3654850. _documentRange seems
4605 // to do the right thing because it works in startSpeaking:, and I know setBackgroundColor: does the
4606 // right thing because I tested it with [self _selectedRange].
4607 // FIXME: This won't actually apply the style to the entire range here, because it ends up calling
4608 // [frame _applyStyle:], which operates on the current selection. To make this work right, we'll
4609 // need to save off the selection, temporarily set it to the entire range, make the change, then
4610 // restore the old selection.
4611 [self _changeCSSColorUsingSelector:@selector(setBackgroundColor:) inRange:[self _documentRange]];
4614 - (void)changeColor:(id)sender
4618 // FIXME: in NSTextView, this method calls changeDocumentBackgroundColor: when a
4619 // private call has earlier been made by [NSFontFontEffectsBox changeColor:], see 3674493.
4620 // AppKit will have to be revised to allow this to work with anything that isn't an
4621 // NSTextView. However, this might not be required for Tiger, since the background-color
4622 // changing box in the font panel doesn't work in Mail (3674481), though it does in TextEdit.
4623 [self _applyStyleToSelection:[self _styleFromColorPanelWithSelector:@selector(setColor:)]
4624 withUndoAction:EditActionSetColor];
4627 - (void)_changeWordCaseWithSelector:(SEL)selector
4629 if (![self _canEdit])
4632 WebFrame *frame = [self _frame];
4633 [self selectWord:nil];
4634 NSString *word = [[frame _selectedString] performSelector:selector];
4635 // FIXME: Does this need a different action context other than "typed"?
4636 if ([self _shouldReplaceSelectionWithText:word givenAction:WebViewInsertActionTyped])
4637 [frame _replaceSelectionWithText:word selectReplacement:NO smartReplace:NO];
4640 - (void)uppercaseWord:(id)sender
4644 [self _changeWordCaseWithSelector:@selector(uppercaseString)];
4647 - (void)lowercaseWord:(id)sender
4651 [self _changeWordCaseWithSelector:@selector(lowercaseString)];
4654 - (void)capitalizeWord:(id)sender
4658 [self _changeWordCaseWithSelector:@selector(capitalizedString)];
4661 - (void)complete:(id)sender
4665 if (![self _canEdit])
4667 if (!_private->completionController)
4668 _private->completionController = [[WebTextCompletionController alloc] initWithWebView:[self _webView] HTMLView:self];
4669 [_private->completionController doCompletion];
4672 - (void)checkSpelling:(id)sender
4676 if (Frame* coreFrame = core([self _frame]))
4677 coreFrame->editor()->advanceToNextMisspelling();
4680 - (void)showGuessPanel:(id)sender
4684 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
4686 LOG_ERROR("No NSSpellChecker");
4690 NSPanel *spellingPanel = [checker spellingPanel];
4691 if ([spellingPanel isVisible]) {
4692 [spellingPanel orderOut:sender];
4696 if (Frame* coreFrame = core([self _frame]))
4697 coreFrame->editor()->advanceToNextMisspelling(true);
4698 [spellingPanel orderFront:sender];
4701 - (void)_changeSpellingToWord:(NSString *)newWord
4703 if (![self _canEdit])
4706 // Don't correct to empty string. (AppKit checked this, we might as well too.)
4707 if (![NSSpellChecker sharedSpellChecker]) {
4708 LOG_ERROR("No NSSpellChecker");
4712 if ([newWord isEqualToString:@""])
4715 if ([self _shouldReplaceSelectionWithText:newWord givenAction:WebViewInsertActionPasted])
4716 [[self _frame] _replaceSelectionWithText:newWord selectReplacement:YES smartReplace:NO];
4719 - (void)changeSpelling:(id)sender
4723 [self _changeSpellingToWord:[[sender selectedCell] stringValue]];
4726 - (void)performFindPanelAction:(id)sender
4730 // Implementing this will probably require copying all of NSFindPanel.h and .m.
4731 // We need *almost* the same thing as AppKit, but not quite.
4732 LOG_ERROR("unimplemented");
4735 - (void)startSpeaking:(id)sender
4739 WebFrame *frame = [self _frame];
4740 DOMRange *range = [self _selectedRange];
4741 if (!range || [range collapsed])
4742 range = [self _documentRange];
4743 [NSApp speakString:[frame _stringForRange:range]];
4746 - (void)stopSpeaking:(id)sender
4750 [NSApp stopSpeaking:sender];
4753 - (void)toggleBaseWritingDirection:(id)sender
4757 if (![self _canEdit])
4760 Frame* coreFrame = core([self _frame]);
4764 WritingDirection direction = RightToLeftWritingDirection;
4765 switch (coreFrame->editor()->baseWritingDirectionForSelectionStart()) {
4766 case NSWritingDirectionLeftToRight:
4768 case NSWritingDirectionRightToLeft:
4769 direction = LeftToRightWritingDirection;
4771 // The writingDirectionForSelectionStart method will never return "natural". It
4772 // will always return a concrete direction. So, keep the compiler happy, and assert not reached.
4773 case NSWritingDirectionNatural:
4774 ASSERT_NOT_REACHED();
4778 if (Frame* coreFrame = core([self _frame]))
4779 coreFrame->editor()->setBaseWritingDirection(direction);
4782 - (void)changeBaseWritingDirection:(id)sender
4786 if (![self _canEdit])
4789 NSWritingDirection writingDirection = static_cast<NSWritingDirection>([sender tag]);
4791 // We disable the menu item that performs this action because we can't implement
4792 // NSWritingDirectionNatural's behavior using CSS.
4793 ASSERT(writingDirection != NSWritingDirectionNatural);
4795 if (Frame* coreFrame = core([self _frame]))
4796 coreFrame->editor()->setBaseWritingDirection(writingDirection == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection);
4799 static BOOL writingDirectionKeyBindingsEnabled()
4801 #ifndef BUILDING_ON_LEOPARD
4804 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
4805 return [defaults boolForKey:@"NSAllowsBaseWritingDirectionKeyBindings"] || [defaults boolForKey:@"AppleTextDirection"];
4809 - (void)_changeBaseWritingDirectionTo:(NSWritingDirection)direction
4811 if (![self _canEdit])
4814 static BOOL bindingsEnabled = writingDirectionKeyBindingsEnabled();
4816 if (!bindingsEnabled) {
4821 if (Frame* coreFrame = core([self _frame]))
4822 coreFrame->editor()->setBaseWritingDirection(direction == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection);
4825 - (void)makeBaseWritingDirectionLeftToRight:(id)sender
4829 [self _changeBaseWritingDirectionTo:NSWritingDirectionLeftToRight];
4832 - (void)makeBaseWritingDirectionRightToLeft:(id)sender
4836 [self _changeBaseWritingDirectionTo:NSWritingDirectionRightToLeft];
4839 #ifdef BUILDING_ON_LEOPARD
4840 - (void)changeBaseWritingDirectionToLTR:(id)sender
4842 [self makeBaseWritingDirectionLeftToRight:sender];
4845 - (void)changeBaseWritingDirectionToRTL:(id)sender
4847 [self makeBaseWritingDirectionRightToLeft:sender];
4851 - (void)makeBaseWritingDirectionNatural:(id)sender
4853 LOG_ERROR("Sent from %@.", sender);
4858 // CSS does not have a way to specify an outline font, which may make this difficult to implement.
4859 // Maybe a special case of text-shadow?
4860 - (void)outline:(id)sender;
4862 // This is part of table support, which may be in NSTextView for Tiger.
4863 // It's probably simple to do the equivalent thing for WebKit.
4864 - (void)insertTable:(id)sender;
4866 // This could be important.
4867 - (void)toggleTraditionalCharacterShape:(id)sender;
4869 // I'm not sure what the equivalents of these in the web world are.
4870 - (void)insertLineSeparator:(id)sender;
4871 - (void)insertPageBreak:(id)sender;
4873 // These methods are not implemented in NSTextView yet at the time of this writing.
4874 - (void)changeCaseOfLetter:(id)sender;
4875 - (void)transposeWords:(id)sender;
4880 // Override this so that AppKit will send us arrow keys as key down events so we can
4881 // support them via the key bindings mechanism.
4882 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
4884 bool haveWebCoreFrame = core([self _frame]);
4886 // If we have a frame, our keyDown method will handle key bindings after sending
4887 // the event through the DOM, so ask AppKit not to do its early special key binding
4888 // mapping. If we don't have a frame, just let things work the normal way without
4890 return haveWebCoreFrame;
4894 - (void)_updateControlTints
4896 Frame* frame = core([self _frame]);
4899 FrameView* view = frame->view();
4902 view->updateControlTints();
4905 // Despite its name, this is called at different times than windowDidBecomeKey is.
4906 // It takes into account all the other factors that determine when NSCell draws
4907 // with different tints, so it's the right call to use for control tints. We'd prefer
4908 // to do this with API. <rdar://problem/5136760>
4909 - (void)_windowChangedKeyState
4911 if (pthread_main_np())
4912 [self _updateControlTints];
4914 [self performSelectorOnMainThread:@selector(_updateControlTints) withObject:nil waitUntilDone:NO];
4916 [super _windowChangedKeyState];
4919 - (void)otherMouseDown:(NSEvent *)event
4921 if ([event buttonNumber] == 2)
4922 [self mouseDown:event];
4924 [super otherMouseDown:event];
4927 - (void)otherMouseDragged:(NSEvent *)event
4929 if ([event buttonNumber] == 2)
4930 [self mouseDragged:event];
4932 [super otherMouseDragged:event];
4935 - (void)otherMouseUp:(NSEvent *)event
4937 if ([event buttonNumber] == 2)
4938 [self mouseUp:event];
4940 [super otherMouseUp:event];
4945 @implementation WebHTMLView (WebInternal)
4947 - (void)_selectionChanged
4949 [self _updateSelectionForInputManager];
4950 [self _updateFontPanel];
4951 if (Frame* coreFrame = core([self _frame]))
4952 coreFrame->editor()->setStartNewKillRingSequence(true);
4955 - (void)_updateFontPanel
4957 // FIXME: NSTextView bails out if becoming or resigning first responder, for which it has ivar flags. Not
4958 // sure if we need to do something similar.
4960 if (![self _canEdit])
4963 NSWindow *window = [self window];
4964 // FIXME: is this first-responder check correct? What happens if a subframe is editable and is first responder?
4965 if (![window isKeyWindow] || [window firstResponder] != self)
4968 bool multipleFonts = false;
4970 if (Frame* coreFrame = core([self _frame])) {
4971 if (const SimpleFontData* fd = coreFrame->editor()->fontForSelection(multipleFonts))
4972 font = fd->getNSFont();
4975 // FIXME: for now, return a bogus font that distinguishes the empty selection from the non-empty
4976 // selection. We should be able to remove this once the rest of this code works properly.
4978 font = [self _hasSelection] ? [NSFont menuFontOfSize:23] : [NSFont toolTipsFontOfSize:17];
4979 ASSERT(font != nil);
4981 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:multipleFonts];
4983 // FIXME: we don't keep track of selected attributes, or set them on the font panel. This
4984 // appears to have no effect on the UI. E.g., underlined text in Mail or TextEdit is
4985 // not reflected in the font panel. Maybe someday this will change.
4988 - (BOOL)_canSmartCopyOrDelete
4990 if (![[self _webView] smartInsertDeleteEnabled])
4992 Frame* coreFrame = core([self _frame]);
4993 return coreFrame && coreFrame->selection()->granularity() == WordGranularity;
4996 - (NSEvent *)_mouseDownEvent
4998 return _private->mouseDownEvent;
5001 - (id<WebHTMLHighlighter>)_highlighterForType:(NSString*)type
5003 return [_private->highlighters objectForKey:type];
5006 - (WebFrame *)_frame
5008 return [_private->dataSource webFrame];
5011 - (void)closeIfNotCurrentView
5013 if ([[[self _frame] frameView] documentView] != self)
5017 - (DOMDocumentFragment*)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
5019 return [self _documentFragmentFromPasteboard:pasteboard inContext:nil allowPlainText:NO];
5023 - (BOOL)isGrammarCheckingEnabled
5025 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
5026 // the AppKit code checks the first responder.
5027 return [[self _webView] isGrammarCheckingEnabled];
5030 - (void)setGrammarCheckingEnabled:(BOOL)flag
5032 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
5033 // the AppKit code checks the first responder.
5034 [[self _webView] setGrammarCheckingEnabled:flag];
5037 - (void)toggleGrammarChecking:(id)sender
5039 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
5040 // the AppKit code checks the first responder.
5041 [[self _webView] toggleGrammarChecking:sender];
5045 static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
5047 NSArray *screens = [NSScreen screens];
5049 if ([screens count] == 0) {
5050 // You could theoretically get here if running with no monitor, in which case it doesn't matter
5051 // much where the "on-screen" point is.
5052 return CGPointMake(point.x, point.y);
5055 // Flip the y coordinate from the top of the menu bar screen -- see 4636390
5056 return CGPointMake(point.x, NSMaxY([(NSScreen *)[screens objectAtIndex:0] frame]) - point.y);
5060 #ifndef BUILDING_ON_LEOPARD
5062 - (void)orderFrontSubstitutionsPanel:(id)sender
5066 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
5068 LOG_ERROR("No NSSpellChecker");
5072 NSPanel *substitutionsPanel = [checker substitutionsPanel];
5073 if ([substitutionsPanel isVisible]) {
5074 [substitutionsPanel orderOut:sender];
5077 [substitutionsPanel orderFront:sender];
5080 // FIXME 4799134: WebView is the bottleneck for this logic, but we must implement these methods here because
5081 // the AppKit code checks the first responder.
5083 - (BOOL)smartInsertDeleteEnabled
5085 return [[self _webView] smartInsertDeleteEnabled];
5088 - (void)setSmartInsertDeleteEnabled:(BOOL)flag
5090 [[self _webView] setSmartInsertDeleteEnabled:flag];
5093 - (void)toggleSmartInsertDelete:(id)sender
5095 [[self _webView] toggleSmartInsertDelete:sender];
5098 - (BOOL)isAutomaticQuoteSubstitutionEnabled
5100 return [[self _webView] isAutomaticQuoteSubstitutionEnabled];
5103 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
5105 [[self _webView] setAutomaticQuoteSubstitutionEnabled:flag];
5108 - (void)toggleAutomaticQuoteSubstitution:(id)sender
5110 [[self _webView] toggleAutomaticQuoteSubstitution:sender];
5113 - (BOOL)isAutomaticLinkDetectionEnabled
5115 return [[self _webView] isAutomaticLinkDetectionEnabled];
5118 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
5120 [[self _webView] setAutomaticLinkDetectionEnabled:flag];
5123 - (void)toggleAutomaticLinkDetection:(id)sender
5125 [[self _webView] toggleAutomaticLinkDetection:sender];
5128 - (BOOL)isAutomaticDashSubstitutionEnabled
5130 return [[self _webView] isAutomaticDashSubstitutionEnabled];
5133 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
5135 [[self _webView] setAutomaticDashSubstitutionEnabled:flag];
5138 - (void)toggleAutomaticDashSubstitution:(id)sender
5140 [[self _webView] toggleAutomaticDashSubstitution:sender];
5143 - (BOOL)isAutomaticTextReplacementEnabled
5145 return [[self _webView] isAutomaticTextReplacementEnabled];
5148 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
5150 [[self _webView] setAutomaticTextReplacementEnabled:flag];
5153 - (void)toggleAutomaticTextReplacement:(id)sender
5155 [[self _webView] toggleAutomaticTextReplacement:sender];
5158 - (BOOL)isAutomaticSpellingCorrectionEnabled
5160 return [[self _webView] isAutomaticSpellingCorrectionEnabled];
5163 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag
5165 [[self _webView] setAutomaticSpellingCorrectionEnabled:flag];
5168 - (void)toggleAutomaticSpellingCorrection:(id)sender
5170 [[self _webView] toggleAutomaticSpellingCorrection:sender];
5175 - (void)_lookUpInDictionaryFromMenu:(id)sender
5177 // Dictionary API will accept a whitespace-only string and display UI as if it were real text,
5178 // so bail out early to avoid that.
5179 if ([[[self selectedString] _webkit_stringByTrimmingWhitespace] length] == 0)
5182 NSAttributedString *attrString = [self selectedAttributedString];
5184 Frame* coreFrame = core([self _frame]);
5188 NSRect rect = coreFrame->selection()->bounds();
5190 NSDictionary *attributes = [attrString fontAttributesInRange:NSMakeRange(0,1)];
5191 NSFont *font = [attributes objectForKey:NSFontAttributeName];
5193 rect.origin.y += [font ascender];
5195 #ifndef BUILDING_ON_LEOPARD
5196 [self showDefinitionForAttributedString:attrString atPoint:rect.origin];
5200 // We soft link to get the function that displays the dictionary (either pop-up window or app) to avoid the performance
5201 // penalty of linking to another framework. This function changed signature as well as framework between Tiger and Leopard,
5202 // so the two cases are handled separately.
5204 typedef void (*ServiceWindowShowFunction)(id unusedDictionaryRef, id inWordString, CFRange selectionRange, id unusedFont, CGPoint textOrigin, Boolean verticalText, id unusedTransform);
5205 const char *frameworkPath = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
5206 const char *functionName = "HIDictionaryWindowShow";
5208 static bool lookedForFunction = false;
5209 static ServiceWindowShowFunction dictionaryServiceWindowShow = NULL;
5211 if (!lookedForFunction) {
5212 void* langAnalysisFramework = dlopen(frameworkPath, RTLD_LAZY);
5213 ASSERT(langAnalysisFramework);
5214 if (langAnalysisFramework)
5215 dictionaryServiceWindowShow = (ServiceWindowShowFunction)dlsym(langAnalysisFramework, functionName);
5216 lookedForFunction = true;
5219 ASSERT(dictionaryServiceWindowShow);
5220 if (!dictionaryServiceWindowShow) {
5221 NSLog(@"Couldn't find the %s function in %s", functionName, frameworkPath);
5225 // The HIDictionaryWindowShow function requires the origin, in CG screen coordinates, of the first character of text in the selection.
5226 // FIXME 4945808: We approximate this in a way that works well when a single word is selected, and less well in some other cases
5227 // (but no worse than we did in Tiger)
5228 NSPoint windowPoint = [self convertPoint:rect.origin toView:nil];
5229 NSPoint screenPoint = [[self window] convertBaseToScreen:windowPoint];
5231 dictionaryServiceWindowShow(nil, attrString, CFRangeMake(0, [attrString length]), nil,
5232 coreGraphicsScreenPointForAppKitScreenPoint(screenPoint), false, nil);
5235 - (void)_hoverFeedbackSuspendedChanged
5237 [self _updateMouseoverWithFakeEvent];
5240 - (void)_executeSavedKeypressCommands
5242 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5243 if (!parameters || parameters->event->keypressCommands().isEmpty())
5246 // We could be called again if the execution of one command triggers a call to selectedRange.
5247 // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result
5248 if (parameters->executingSavedKeypressCommands)
5251 // Avoid an infinite loop that would occur if executing a command appended it to event->keypressCommands() again.
5252 bool wasSavingCommands = parameters->shouldSaveCommands;
5253 parameters->shouldSaveCommands = false;
5254 parameters->executingSavedKeypressCommands = true;
5256 const Vector<KeypressCommand>& commands = parameters->event->keypressCommands();
5258 for (size_t i = 0; i < commands.size(); ++i) {
5259 if (commands[i].commandName == "insertText:")
5260 [self insertText:commands[i].text];
5261 else if (commands[i].commandName == "noop:")
5262 ; // Do nothing. This case can be removed once <rdar://problem/9025012> is fixed.
5264 [self doCommandBySelector:NSSelectorFromString(commands[i].commandName)];
5266 parameters->event->keypressCommands().clear();
5267 parameters->shouldSaveCommands = wasSavingCommands;
5268 parameters->executingSavedKeypressCommands = false;
5271 - (BOOL)_interpretKeyEvent:(KeyboardEvent*)event savingCommands:(BOOL)savingCommands
5273 ASSERT(core([self _frame]) == event->target()->toNode()->document()->frame());
5274 ASSERT(!savingCommands || event->keypressCommands().isEmpty()); // Save commands once for each event.
5276 WebHTMLViewInterpretKeyEventsParameters parameters;
5277 parameters.eventInterpretationHadSideEffects = false;
5278 parameters.shouldSaveCommands = savingCommands;
5279 parameters.executingSavedKeypressCommands = false;
5280 // If we're intercepting the initial IM call we assume that the IM has consumed the event,
5281 // and only change this assumption if one of the NSTextInput/Responder callbacks is used.
5282 // We assume the IM will *not* consume hotkey sequences
5283 parameters.consumedByIM = savingCommands && !event->metaKey();
5285 const PlatformKeyboardEvent* platformEvent = event->keyEvent();
5289 NSEvent *macEvent = platformEvent->macEvent();
5290 if ([macEvent type] == NSKeyDown && [_private->completionController filterKeyDown:macEvent])
5293 if ([macEvent type] == NSFlagsChanged)
5296 parameters.event = event;
5297 _private->interpretKeyEventsParameters = ¶meters;
5298 const Vector<KeypressCommand>& commands = event->keypressCommands();
5300 if (savingCommands) {
5301 // AppKit will respond with a series of NSTextInput protocol method calls. There are three groups that we heuristically differentiate:
5302 // 1. Key Bindings. Only doCommandBySelector: and insertText: calls will be made, which we save in the event for execution
5303 // after DOM dispatch. This is safe, because neither returns a result, so there is no branching on AppKit side.
5304 // 2. Plain text input. Here as well, we need to dispatch DOM events prior to inserting text, so we save the insertText: command.
5305 // 3. Input method processing. An IM can make any NSTextInput calls, and can base its decisions on results it gets, so we must
5306 // execute the calls immediately. DOM events like keydown are tweaked to have keyCode of 229, and canceling them has no effect.
5307 // Unfortunately, there is no real difference between plain text input and IM processing - for example, AppKit queries hasMarkedText
5308 // when typing with U.S. keyboard, and inserts marked text for dead keys.
5309 [self interpretKeyEvents:[NSArray arrayWithObject:macEvent]];
5311 // Are there commands that could just cause text insertion if executed via Editor?
5312 // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
5313 // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
5314 // (e.g. Tab that inserts a Tab character, or Enter).
5315 bool haveTextInsertionCommands = false;
5316 for (size_t i = 0; i < commands.size(); ++i) {
5317 if ([self coreCommandBySelector:NSSelectorFromString(commands[i].commandName)].isTextInsertion())
5318 haveTextInsertionCommands = true;
5320 // If there are no text insertion commands, default keydown handler is the right time to execute the commands.
5321 // Keypress (Char event) handler is the latest opportunity to execute.
5322 if (!haveTextInsertionCommands || platformEvent->type() == PlatformKeyboardEvent::Char)
5323 [self _executeSavedKeypressCommands];
5325 _private->interpretKeyEventsParameters = 0;
5327 // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
5328 // IM-like actions are handled immediately (so parameters.eventInterpretationHadSideEffects is true), but there are saved commands that
5329 // should be handled like normal text input after DOM event dispatch.
5330 if (!event->keypressCommands().isEmpty())
5333 // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
5334 // in which case we should not bubble the event up the DOM.
5335 if (parameters.consumedByIM)
5338 // If we have already executed all commands, don't do it again.
5339 return parameters.eventInterpretationHadSideEffects;
5342 - (WebCore::CachedImage*)promisedDragTIFFDataSource
5344 return _private->promisedDragTIFFDataSource;
5347 - (void)setPromisedDragTIFFDataSource:(WebCore::CachedImage*)source
5350 source->addClient(promisedDataClient());
5352 if (_private->promisedDragTIFFDataSource)
5353 _private->promisedDragTIFFDataSource->removeClient(promisedDataClient());
5354 _private->promisedDragTIFFDataSource = source;
5357 #undef COMMAND_PROLOGUE
5359 - (void)_layoutIfNeeded
5361 ASSERT(!_private->subviewsSetAside);
5363 if ([self _needsLayout])
5367 - (void)_web_updateLayoutAndStyleIfNeededRecursive
5369 WebFrame *webFrame = [self _frame];
5370 Frame* coreFrame = core(webFrame);
5371 if (coreFrame && coreFrame->view())
5372 coreFrame->view()->updateLayoutAndStyleIfNeededRecursive();
5375 - (void) _destroyAllWebPlugins
5377 [[self _pluginController] destroyAllPlugins];
5380 - (BOOL)_needsLayout
5382 return [[self _frame] _needsLayout];
5385 #if USE(ACCELERATED_COMPOSITING)
5386 - (void)attachRootLayer:(CALayer*)layer
5388 if (!_private->layerHostingView) {
5389 NSView* hostingView = [[NSView alloc] initWithFrame:[self bounds]];
5390 #ifndef BUILDING_ON_LEOPARD
5391 [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
5393 [self addSubview:hostingView];
5394 [hostingView release];
5395 // hostingView is owned by being a subview of self
5396 _private->layerHostingView = hostingView;
5399 // Make a container layer, which will get sized/positioned by AppKit and CA.
5400 CALayer* viewLayer = [CALayer layer];
5402 #ifdef BUILDING_ON_LEOPARD
5403 // Turn off default animations.
5404 NSNull *nullValue = [NSNull null];
5405 NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys:
5406 nullValue, @"anchorPoint",
5407 nullValue, @"bounds",
5408 nullValue, @"contents",
5409 nullValue, @"contentsRect",
5410 nullValue, @"opacity",
5411 nullValue, @"position",
5412 nullValue, @"sublayerTransform",
5413 nullValue, @"sublayers",
5414 nullValue, @"transform",
5416 [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]];
5420 // If we are in a layer-backed view, we need to manually initialize the geometry for our layer.
5421 [viewLayer setBounds:NSRectToCGRect([_private->layerHostingView bounds])];
5422 [viewLayer setAnchorPoint:CGPointMake(0, [self isFlipped] ? 1 : 0)];
5423 CGPoint layerPosition = NSPointToCGPoint([self convertPointToBase:[_private->layerHostingView frame].origin]);
5424 [viewLayer setPosition:layerPosition];
5427 [_private->layerHostingView setLayer:viewLayer];
5428 [_private->layerHostingView setWantsLayer:YES];
5430 // Parent our root layer in the container layer
5431 [viewLayer addSublayer:layer];
5433 if ([[self _webView] _postsAcceleratedCompositingNotifications])
5434 [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:[self _webView] userInfo:nil];
5436 #ifdef BUILDING_ON_LEOPARD
5437 [viewLayer setSublayerTransform:CATransform3DMakeScale(1, -1, 1)]; // setGeometryFlipped: doesn't exist on Leopard.
5438 [self _updateLayerHostingViewPosition];
5440 // Do geometry flipping here, which flips all the compositing layers so they are top-down.
5441 [viewLayer setGeometryFlipped:YES];
5445 - (void)detachRootLayer
5447 if (_private->layerHostingView) {
5448 [_private->layerHostingView setLayer:nil];
5449 [_private->layerHostingView setWantsLayer:NO];
5450 [_private->layerHostingView removeFromSuperview];
5451 _private->layerHostingView = nil;
5455 #ifdef BUILDING_ON_LEOPARD
5456 // This method is necessary on Leopard to work around <rdar://problem/7067892>.
5457 - (void)_updateLayerHostingViewPosition
5459 if (!_private->layerHostingView)
5462 const CGFloat maxHeight = 2048;
5463 NSRect layerViewFrame = [self bounds];
5465 if (layerViewFrame.size.height > maxHeight) {
5466 // Clamp the size of the view to <= maxHeight to avoid the bug.
5467 layerViewFrame.size.height = maxHeight;
5468 NSRect visibleRect = [[self enclosingScrollView] documentVisibleRect];
5470 // Place the top of the layer-hosting view at the top of the visibleRect.
5471 CGFloat topOffset = NSMinY(visibleRect);
5472 layerViewFrame.origin.y = topOffset;
5474 // Compensate for the moved view by adjusting the sublayer transform on the view's layer (using flipped coords).
5475 CATransform3D flipTransform = CATransform3DMakeTranslation(0, topOffset, 0);
5476 flipTransform = CATransform3DScale(flipTransform, 1, -1, 1);
5477 [[_private->layerHostingView layer] setSublayerTransform:flipTransform];
5480 [_private->layerHostingView _updateLayerGeometryFromView]; // Workaround for <rdar://problem/7071636>
5481 [_private->layerHostingView setFrame:layerViewFrame];
5483 #endif // defined(BUILDING_ON_LEOPARD)
5485 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
5488 ASSERT(!_private->drawingIntoLayer);
5489 _private->drawingIntoLayer = YES;
5492 [super drawLayer:layer inContext:ctx];
5495 _private->drawingIntoLayer = NO;
5498 - (BOOL)_web_isDrawingIntoLayer
5500 return _private->drawingIntoLayer;
5503 #endif // USE(ACCELERATED_COMPOSITING)
5507 @implementation WebHTMLView (WebNSTextInputSupport)
5509 - (NSArray *)validAttributesForMarkedText
5511 static NSArray *validAttributes;
5512 if (!validAttributes) {
5513 validAttributes = [[NSArray alloc] initWithObjects:
5514 NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName,
5515 NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
5516 // NSText also supports the following attributes, but it's
5517 // hard to tell which are really required for text input to
5518 // work well; I have not seen any input method make use of them yet.
5519 // NSFontAttributeName, NSForegroundColorAttributeName,
5520 // NSBackgroundColorAttributeName, NSLanguageAttributeName.
5521 CFRetain(validAttributes);
5523 LOG(TextInput, "validAttributesForMarkedText -> (...)");
5524 return validAttributes;
5527 - (NSTextInputContext *)inputContext
5529 return _private->exposeInputContext ? [super inputContext] : nil;
5532 - (NSAttributedString *)textStorage
5534 if (!_private->exposeInputContext) {
5535 LOG(TextInput, "textStorage -> nil");
5538 NSAttributedString *result = [self attributedSubstringFromRange:NSMakeRange(0, UINT_MAX)];
5540 LOG(TextInput, "textStorage -> \"%@\"", result ? [result string] : @"");
5542 // We have to return an empty string rather than null to prevent TSM from calling -string
5543 return result ? result : [[[NSAttributedString alloc] initWithString:@""] autorelease];
5546 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
5548 [self _executeSavedKeypressCommands];
5550 NSWindow *window = [self window];
5551 WebFrame *frame = [self _frame];
5554 thePoint = [window convertScreenToBase:thePoint];
5555 thePoint = [self convertPoint:thePoint fromView:nil];
5557 DOMRange *range = [frame _characterRangeAtPoint:thePoint];
5559 LOG(TextInput, "characterIndexForPoint:(%f, %f) -> NSNotFound", thePoint.x, thePoint.y);
5563 unsigned result = [frame _convertDOMRangeToNSRange:range].location;
5564 LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
5568 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
5570 [self _executeSavedKeypressCommands];
5572 WebFrame *frame = [self _frame];
5574 // Just to match NSTextView's behavior. Regression tests cannot detect this;
5575 // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
5576 // (type something; try ranges (1, -1) and (2, -1).
5577 if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
5578 theRange.length = 0;
5580 DOMRange *range = [frame _convertNSRangeToDOMRange:theRange];
5582 LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (0, 0, 0, 0)", theRange.location, theRange.length);
5583 return NSMakeRect(0, 0, 0, 0);
5586 ASSERT([range startContainer]);
5587 ASSERT([range endContainer]);
5589 NSRect resultRect = [frame _firstRectForDOMRange:range];
5590 resultRect = [self convertRect:resultRect toView:nil];
5592 NSWindow *window = [self window];
5594 resultRect.origin = [window convertBaseToScreen:resultRect.origin];
5596 LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
5600 - (NSRange)selectedRange
5602 [self _executeSavedKeypressCommands];
5604 if (!isTextInput(core([self _frame]))) {
5605 LOG(TextInput, "selectedRange -> (NSNotFound, 0)");
5606 return NSMakeRange(NSNotFound, 0);
5608 NSRange result = [[self _frame] _selectedNSRange];
5610 LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length);
5614 - (NSRange)markedRange
5616 [self _executeSavedKeypressCommands];
5618 WebFrame *webFrame = [self _frame];
5619 Frame* coreFrame = core(webFrame);
5621 return NSMakeRange(0, 0);
5622 NSRange result = [webFrame _convertToNSRange:coreFrame->editor()->compositionRange().get()];
5624 LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
5628 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange
5630 [self _executeSavedKeypressCommands];
5632 WebFrame *frame = [self _frame];
5633 Frame* coreFrame = core(frame);
5634 if (!isTextInput(coreFrame) || isInPasswordField(coreFrame)) {
5635 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
5638 RefPtr<Range> range = [frame _convertToDOMRange:nsRange];
5640 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
5644 NSAttributedString *result = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
5646 // [WebHTMLConverter editingAttributedStringFromRange:] insists on inserting a trailing
5647 // whitespace at the end of the string which breaks the ATOK input method. <rdar://problem/5400551>
5648 // To work around this we truncate the resultant string to the correct length.
5649 if ([result length] > nsRange.length) {
5650 ASSERT([result length] == nsRange.length + 1);
5651 ASSERT([[result string] characterAtIndex:nsRange.length] == '\n' || [[result string] characterAtIndex:nsRange.length] == ' ');
5652 result = [result attributedSubstringFromRange:NSMakeRange(0, nsRange.length)];
5654 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result string]);
5658 - (NSInteger)conversationIdentifier
5660 return (NSInteger)self;
5663 - (BOOL)hasMarkedText
5665 Frame* coreFrame = core([self _frame]);
5666 BOOL result = coreFrame && coreFrame->editor()->hasComposition();
5669 // A saved command can confirm a composition, but it cannot start a new one.
5670 [self _executeSavedKeypressCommands];
5671 result = coreFrame->editor()->hasComposition();
5674 LOG(TextInput, "hasMarkedText -> %u", result);
5680 [self _executeSavedKeypressCommands];
5682 LOG(TextInput, "unmarkText");
5684 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
5685 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5688 parameters->eventInterpretationHadSideEffects = true;
5689 parameters->consumedByIM = false;
5692 if (Frame* coreFrame = core([self _frame]))
5693 coreFrame->editor()->confirmComposition();
5696 static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
5698 int length = [[string string] length];
5701 while (i < length) {
5703 NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
5705 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
5706 Color color = Color::black;
5707 if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
5708 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
5709 result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
5712 i = range.location + range.length;
5716 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
5718 [self _executeSavedKeypressCommands];
5720 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
5721 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
5723 LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length);
5725 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
5726 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5729 parameters->eventInterpretationHadSideEffects = true;
5730 parameters->consumedByIM = false;
5733 Frame* coreFrame = core([self _frame]);
5737 if (![self _isEditable])
5740 Vector<CompositionUnderline> underlines;
5742 NSRange replacementRange = { NSNotFound, 0 };
5744 if (isAttributedString) {
5745 // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
5746 text = [string string];
5747 NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])];
5748 LOG(TextInput, " ReplacementRange: %@", rangeString);
5749 // The AppKit adds a 'secret' property to the string that contains the replacement range.
5750 // The replacement range is the range of the the text that should be replaced with the new string.
5752 replacementRange = NSRangeFromString(rangeString);
5754 extractUnderlines(string, underlines);
5758 if (replacementRange.location != NSNotFound)
5759 [[self _frame] _selectNSRange:replacementRange];
5761 coreFrame->editor()->setComposition(text, underlines, newSelRange.location, NSMaxRange(newSelRange));
5764 - (void)doCommandBySelector:(SEL)selector
5766 LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
5768 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
5769 // The same call to interpretKeyEvents can do more than one command.
5770 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5772 parameters->consumedByIM = false;
5774 KeyboardEvent* event = parameters ? parameters->event : 0;
5775 bool shouldSaveCommand = parameters && parameters->shouldSaveCommands;
5777 // As in insertText:, we assume that the call comes from an input method if there is marked text.
5778 RefPtr<Frame> coreFrame = core([self _frame]);
5779 bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition();
5781 if (event && shouldSaveCommand && !isFromInputMethod)
5782 event->keypressCommands().append(KeypressCommand(NSStringFromSelector(selector)));
5784 // Make sure that only direct calls to doCommandBySelector: see the parameters by setting to 0.
5785 _private->interpretKeyEventsParameters = 0;
5787 bool eventWasHandled;
5789 WebView *webView = [self _webView];
5790 if ([[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector])
5791 eventWasHandled = true;
5793 Editor::Command command = [self coreCommandBySelector:selector];
5794 if (command.isSupported())
5795 eventWasHandled = command.execute(event);
5797 // If WebKit does not support this command, we need to pass the selector to super.
5798 _private->selectorForDoCommandBySelector = selector;
5800 // The sink does two things: 1) Tells us if the responder went unhandled, and
5801 // 2) prevents any NSBeep; we don't ever want to beep here.
5802 WebResponderChainSink *sink = [[WebResponderChainSink alloc] initWithResponderChain:self];
5803 [super doCommandBySelector:selector];
5804 eventWasHandled = ![sink receivedUnhandledCommand];
5808 _private->selectorForDoCommandBySelector = 0;
5813 parameters->eventInterpretationHadSideEffects |= eventWasHandled;
5815 _private->interpretKeyEventsParameters = parameters;
5819 - (void)insertText:(id)string
5821 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
5822 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
5824 LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
5826 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5828 parameters->consumedByIM = false;
5830 RefPtr<Frame> coreFrame = core([self _frame]);
5832 NSRange replacementRange = { NSNotFound, 0 };
5833 bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition();
5835 if (isAttributedString) {
5836 // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
5837 // It does not look like any input methods ever use insertText: with attributes other than NSTextInputReplacementRangeAttributeName.
5838 text = [string string];
5839 NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])];
5840 LOG(TextInput, " ReplacementRange: %@", rangeString);
5842 replacementRange = NSRangeFromString(rangeString);
5843 isFromInputMethod = true;
5848 KeyboardEvent* event = parameters ? parameters->event : 0;
5850 // insertText can be called for several reasons:
5851 // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later.
5852 // - If it's from an input method, then we should go ahead and insert the text now. We assume it's from the input method if we have marked text.
5853 // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method.
5854 // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
5855 // then we also execute it immediately, as there will be no other chance.
5856 bool shouldSaveCommand = parameters && parameters->shouldSaveCommands;
5857 if (event && shouldSaveCommand && !isFromInputMethod) {
5858 event->keypressCommands().append(KeypressCommand("insertText:", text));
5862 if (!coreFrame || !coreFrame->editor()->canEdit())
5865 if (replacementRange.location != NSNotFound)
5866 [[self _frame] _selectNSRange:replacementRange];
5868 bool eventHandled = false;
5869 String eventText = text;
5870 eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
5871 if (!coreFrame->editor()->hasComposition()) {
5872 // An insertText: might be handled by other responders in the chain if we don't handle it.
5873 // One example is space bar that results in scrolling down the page.
5874 eventHandled = coreFrame->editor()->insertText(eventText, event);
5876 eventHandled = true;
5877 coreFrame->editor()->confirmComposition(eventText);
5881 parameters->eventInterpretationHadSideEffects |= eventHandled;
5884 - (void)_updateSecureInputState
5886 if (![[self window] isKeyWindow] || ([[self window] firstResponder] != self && !_private->_forceUpdateSecureInputState)) {
5887 if (_private->isInSecureInputState) {
5888 DisableSecureEventInput();
5889 _private->isInSecureInputState = NO;
5894 Frame* coreFrame = core([self _frame]);
5898 if (isInPasswordField(coreFrame)) {
5899 if (!_private->isInSecureInputState)
5900 EnableSecureEventInput();
5901 _private->isInSecureInputState = YES;
5902 // WebKit substitutes nil for input context when in password field, which corresponds to null TSMDocument. So, there is
5903 // no need to call TSMGetActiveDocument(), which may return an incorrect result when selection hasn't been yet updated
5904 // after focusing a node.
5905 static CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
5906 TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag, sizeof(CFArrayRef), &inputSources);
5908 if (_private->isInSecureInputState)
5909 DisableSecureEventInput();
5910 _private->isInSecureInputState = NO;
5911 TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
5915 - (void)_updateSelectionForInputManager
5917 Frame* coreFrame = core([self _frame]);
5921 BOOL exposeInputContext = isTextInput(coreFrame) && !isInPasswordField(coreFrame);
5922 if (exposeInputContext != _private->exposeInputContext) {
5923 _private->exposeInputContext = exposeInputContext;
5924 // Let AppKit cache a potentially changed input context.
5925 // WebCore routinely sets the selection to None when editing, and IMs become unhappy when an input context suddenly turns nil, see bug 26009.
5926 if (!coreFrame->selection()->isNone())
5927 [NSApp updateWindows];
5930 [self _updateSecureInputState];
5932 if (!coreFrame->editor()->hasComposition())
5935 if (coreFrame->editor()->ignoreCompositionSelectionChange())
5940 if (coreFrame->editor()->getCompositionSelection(start, end))
5941 [[NSInputManager currentInputManager] markedTextSelectionChanged:NSMakeRange(start, end - start) client:self];
5943 coreFrame->editor()->cancelComposition();
5944 [[NSInputManager currentInputManager] markedTextAbandoned:self];
5950 @implementation WebHTMLView (WebDocumentPrivateProtocols)
5952 - (NSRect)selectionRect
5954 if (![self _hasSelection])
5956 return core([self _frame])->selection()->bounds();
5959 - (NSArray *)selectionTextRects
5961 if (![self _hasSelection])
5964 Vector<FloatRect> list;
5965 if (Frame* coreFrame = core([self _frame]))
5966 coreFrame->selection()->getClippedVisibleTextRectangles(list);
5968 size_t size = list.size();
5970 NSMutableArray *result = [NSMutableArray arrayWithCapacity:size];
5972 for (size_t i = 0; i < size; ++i)
5973 [result addObject:[NSValue valueWithRect:list[i]]];
5978 - (NSView *)selectionView
5983 - (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText
5985 if (![self _hasSelection])
5987 return core([self _frame])->selectionImage(forceBlackText);
5990 - (NSRect)selectionImageRect
5992 if (![self _hasSelection])
5994 return core([self _frame])->selection()->bounds();
5997 - (NSArray *)pasteboardTypesForSelection
5999 if ([self _canSmartCopyOrDelete]) {
6000 NSMutableArray *types = [[[[self class] _selectionPasteboardTypes] mutableCopy] autorelease];
6001 [types addObject:WebSmartPastePboardType];
6004 return [[self class] _selectionPasteboardTypes];
6008 - (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
6010 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:nil];
6015 Frame* coreFrame = core([self _frame]);
6017 coreFrame->selection()->selectAll();
6022 Frame* coreFrame = core([self _frame]);
6025 coreFrame->selection()->clear();
6028 - (NSString *)string
6030 return [[self _frame] _stringForRange:[self _documentRange]];
6033 - (NSAttributedString *)_attributeStringFromDOMRange:(DOMRange *)range
6035 NSAttributedString *attributedString;
6037 double start = CFAbsoluteTimeGetCurrent();
6039 attributedString = [[[NSAttributedString alloc] _initWithDOMRange:range] autorelease];
6041 double duration = CFAbsoluteTimeGetCurrent() - start;
6042 LOG(Timing, "creating attributed string from selection took %f seconds.", duration);
6044 return attributedString;
6047 - (NSAttributedString *)attributedString
6049 DOMDocument *document = [[self _frame] DOMDocument];
6050 NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[document _documentRange]];
6051 if (!attributedString) {
6052 Document* coreDocument = core(document);
6053 attributedString = [WebHTMLConverter editingAttributedStringFromRange:Range::create(coreDocument, coreDocument, 0, coreDocument, coreDocument->childNodeCount()).get()];
6055 return attributedString;
6058 - (NSString *)selectedString
6060 return [[self _frame] _selectedString];
6063 - (NSAttributedString *)selectedAttributedString
6065 NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[self _selectedRange]];
6066 if (!attributedString) {
6067 Frame* coreFrame = core([self _frame]);
6069 RefPtr<Range> range = coreFrame->selection()->selection().toNormalizedRange();
6070 attributedString = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
6073 return attributedString;
6076 - (BOOL)supportsTextEncoding
6081 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
6083 return [self _findString:string options:(forward ? 0 : WebFindOptionsBackwards) | (caseFlag ? 0 : WebFindOptionsCaseInsensitive) | (wrapFlag ? WebFindOptionsWrapAround : 0) | (startInSelection ? WebFindOptionsStartInSelection : 0)];
6088 @implementation WebHTMLView (WebDocumentInternalProtocols)
6090 - (NSDictionary *)elementAtPoint:(NSPoint)point
6092 return [self elementAtPoint:point allowShadowContent:NO];
6095 - (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow
6097 Frame* coreFrame = core([self _frame]);
6100 return [[[WebElementDictionary alloc] initWithHitTestResult:coreFrame->eventHandler()->hitTestResultAtPoint(IntPoint(point), allow)] autorelease];
6103 - (NSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options limit:(NSUInteger)limit markMatches:(BOOL)markMatches
6105 Frame* coreFrame = core([self _frame]);
6109 return coreFrame->editor()->countMatchesForText(string, core(range), coreOptions(options), limit, markMatches);
6112 - (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
6114 Frame* coreFrame = core([self _frame]);
6117 coreFrame->editor()->setMarkedTextMatchesAreHighlighted(newValue);
6120 - (BOOL)markedTextMatchesAreHighlighted
6122 Frame* coreFrame = core([self _frame]);
6123 return coreFrame && coreFrame->editor()->markedTextMatchesAreHighlighted();
6126 - (void)unmarkAllTextMatches
6128 Frame* coreFrame = core([self _frame]);
6131 Document* document = coreFrame->document();
6134 document->markers()->removeMarkers(DocumentMarker::TextMatch);
6137 - (NSArray *)rectsForTextMatches
6139 Frame* coreFrame = core([self _frame]);
6141 return [NSArray array];
6142 Document* document = coreFrame->document();
6144 return [NSArray array];
6146 Vector<IntRect> rects = document->markers()->renderedRectsForMarkers(DocumentMarker::TextMatch);
6147 unsigned count = rects.size();
6148 NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
6149 for (unsigned index = 0; index < count; ++index)
6150 [result addObject:[NSValue valueWithRect:rects[index]]];
6154 - (BOOL)_findString:(NSString *)string options:(WebFindOptions)options
6156 if (![string length])
6158 Frame* coreFrame = core([self _frame]);
6159 return coreFrame && coreFrame->editor()->findString(string, coreOptions(options));
6164 // This is used by AppKit and is included here so that WebDataProtocolScheme is only defined once.
6165 @implementation NSURL (WebDataURL)
6167 + (NSURL *)_web_uniqueWebDataURL
6169 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
6170 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
6172 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", WebDataProtocolScheme, UUIDString]];
6173 CFRelease(UUIDString);
6179 @implementation WebResponderChainSink
6181 - (id)initWithResponderChain:(NSResponder *)chain
6183 self = [super init];
6184 _lastResponderInChain = chain;
6185 while (NSResponder *next = [_lastResponderInChain nextResponder])
6186 _lastResponderInChain = next;
6187 [_lastResponderInChain setNextResponder:self];
6193 [_lastResponderInChain setNextResponder:nil];
6194 _lastResponderInChain = nil;
6197 - (BOOL)receivedUnhandledCommand
6199 return _receivedUnhandledCommand;
6202 - (void)noResponderFor:(SEL)selector
6204 _receivedUnhandledCommand = YES;
6207 - (void)doCommandBySelector:(SEL)selector
6209 _receivedUnhandledCommand = YES;
6212 - (BOOL)tryToPerform:(SEL)action with:(id)object
6214 _receivedUnhandledCommand = YES;