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/PlatformEventFactory.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 @interface WebLayerHostingFlippedView : NSView
207 @implementation WebLayerHostingFlippedView
214 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
215 static BOOL forceNSViewHitTest;
217 // 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])
218 static BOOL forceWebHTMLViewHitTest;
220 static WebHTMLView *lastHitView;
222 static bool needsCursorRectsSupportAtPoint(NSWindow* window, NSPoint point)
224 forceNSViewHitTest = YES;
225 NSView* view = [[window _web_borderView] hitTest:point];
226 forceNSViewHitTest = NO;
228 // WebHTMLView doesn't use cursor rects.
229 if ([view isKindOfClass:[WebHTMLView class]])
232 #if ENABLE(NETSCAPE_PLUGIN_API)
233 // Neither do NPAPI plug-ins.
234 if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
238 // Non-Web content, WebPDFView, and WebKit plug-ins use normal cursor handling.
243 static IMP oldSetCursorForMouseLocationIMP;
245 // Overriding an internal method is a hack; <rdar://problem/7662987> tracks finding a better solution.
246 static void setCursor(NSWindow *self, SEL cmd, NSPoint point)
248 if (needsCursorRectsSupportAtPoint(self, point))
249 oldSetCursorForMouseLocationIMP(self, cmd, point);
255 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
257 extern NSString *NSMarkedClauseSegmentAttributeName;
258 extern NSString *NSTextInputReplacementRangeAttributeName;
262 @interface NSView (WebNSViewDetails)
263 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
264 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
265 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView;
266 - (NSRect)_dirtyRect;
267 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
268 - (BOOL)_drawnByAncestor;
269 - (void)_invalidateGStatesForTree;
270 - (void)_propagateDirtyRectsToOpaqueAncestors;
271 - (void)_windowChangedKeyState;
272 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
273 - (void)_updateLayerGeometryFromView;
277 #if USE(ACCELERATED_COMPOSITING)
278 static IMP oldSetNeedsDisplayInRectIMP;
280 static void setNeedsDisplayInRect(NSView *self, SEL cmd, NSRect invalidRect)
282 if (![self _drawnByAncestor]) {
283 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
287 static Class webFrameViewClass = [WebFrameView class];
288 WebFrameView *enclosingWebFrameView = (WebFrameView *)self;
289 while (enclosingWebFrameView && ![enclosingWebFrameView isKindOfClass:webFrameViewClass])
290 enclosingWebFrameView = (WebFrameView *)[enclosingWebFrameView superview];
292 if (!enclosingWebFrameView) {
293 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
297 Frame* coreFrame = core([enclosingWebFrameView webFrame]);
298 FrameView* frameView = coreFrame ? coreFrame->view() : 0;
299 if (!frameView || !frameView->isEnclosedInCompositingLayer()) {
300 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
304 NSRect invalidRectInWebFrameViewCoordinates = [enclosingWebFrameView convertRect:invalidRect fromView:self];
305 IntRect invalidRectInFrameViewCoordinates(invalidRectInWebFrameViewCoordinates);
306 if (![enclosingWebFrameView isFlipped])
307 invalidRectInFrameViewCoordinates.setY(frameView->frameRect().size().height() - invalidRectInFrameViewCoordinates.maxY());
309 frameView->invalidateRect(invalidRectInFrameViewCoordinates);
311 #endif // USE(ACCELERATED_COMPOSITING)
313 @interface NSApplication (WebNSApplicationDetails)
314 - (void)speakString:(NSString *)string;
317 @interface NSWindow (WebNSWindowDetails)
318 - (id)_newFirstResponderAfterResigning;
321 @interface NSAttributedString (WebNSAttributedStringDetails)
322 - (id)_initWithDOMRange:(DOMRange *)range;
323 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
326 @interface NSSpellChecker (WebNSSpellCheckerDetails)
327 - (void)learnWord:(NSString *)word;
330 // By imaging to a width a little wider than the available pixels,
331 // thin pages will be scaled down a little, matching the way they
332 // print in IE and Camino. This lets them use fewer sheets than they
333 // would otherwise, which is presumably why other browsers do this.
334 // Wide pages will be scaled down more than this.
335 const float _WebHTMLViewPrintingMinimumShrinkFactor = 1.25;
337 // This number determines how small we are willing to reduce the page content
338 // in order to accommodate the widest line. If the page would have to be
339 // reduced smaller to make the widest line fit, we just clip instead (this
340 // behavior matches MacIE and Mozilla, at least)
341 const float _WebHTMLViewPrintingMaximumShrinkFactor = 2;
343 #define AUTOSCROLL_INTERVAL 0.1f
345 // Any non-zero value will do, but using something recognizable might help us debug some day.
346 #define TRACKING_RECT_TAG 0xBADFACE
348 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
349 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
351 #define STANDARD_WEIGHT 5
352 #define MIN_BOLD_WEIGHT 7
353 #define STANDARD_BOLD_WEIGHT 9
356 #define WebDataProtocolScheme @"webkit-fake-url"
358 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
359 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
360 @interface WebCoreScrollView : NSScrollView
363 @implementation WebCoreScrollView
366 // We need this to be able to safely reference the CachedImage for the promised drag data
367 static CachedImageClient* promisedDataClient()
369 static CachedImageClient* staticCachedResourceClient = new CachedImageClient;
370 return staticCachedResourceClient;
373 @interface WebHTMLView (WebHTMLViewFileInternal)
374 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
375 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
376 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
377 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
378 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
379 - (void)_removeMouseMovedObserverUnconditionally;
380 - (void)_removeSuperviewObservers;
381 - (void)_removeWindowObservers;
382 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
383 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
384 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
385 - (DOMRange *)_selectedRange;
386 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
387 - (NSView *)_hitViewForEvent:(NSEvent *)event;
388 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
389 - (DOMRange *)_documentRange;
390 - (void)_setMouseDownEvent:(NSEvent *)event;
391 - (WebHTMLView *)_topHTMLView;
392 - (BOOL)_isTopHTMLView;
393 - (void)_web_setPrintingModeRecursive;
394 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
395 - (void)_web_clearPrintingModeRecursive;
398 #ifndef BUILDING_ON_LEOPARD
400 @interface WebHTMLView (WebHTMLViewTextCheckingInternal)
401 - (void)orderFrontSubstitutionsPanel:(id)sender;
402 - (BOOL)smartInsertDeleteEnabled;
403 - (void)setSmartInsertDeleteEnabled:(BOOL)flag;
404 - (void)toggleSmartInsertDelete:(id)sender;
405 - (BOOL)isAutomaticQuoteSubstitutionEnabled;
406 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag;
407 - (void)toggleAutomaticQuoteSubstitution:(id)sender;
408 - (BOOL)isAutomaticLinkDetectionEnabled;
409 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag;
410 - (void)toggleAutomaticLinkDetection:(id)sender;
411 - (BOOL)isAutomaticDashSubstitutionEnabled;
412 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag;
413 - (void)toggleAutomaticDashSubstitution:(id)sender;
414 - (BOOL)isAutomaticTextReplacementEnabled;
415 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag;
416 - (void)toggleAutomaticTextReplacement:(id)sender;
417 - (BOOL)isAutomaticSpellingCorrectionEnabled;
418 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag;
419 - (void)toggleAutomaticSpellingCorrection:(id)sender;
424 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
425 - (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageWidth logicalHeight:(float)minPageHeight originalPageWidth:(float)pageLogicalWidth originalPageHeight:(float)pageLogicalHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent;
426 - (void)_updateSecureInputState;
429 @class NSTextInputContext;
430 @interface NSResponder (AppKitDetails)
431 - (NSTextInputContext *)inputContext;
434 @interface NSObject (NSTextInputContextDetails)
435 - (BOOL)wantsToHandleMouseEvents;
436 - (BOOL)handleMouseEvent:(NSEvent *)event;
439 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
440 - (void)_updateSelectionForInputManager;
443 @interface WebHTMLView (WebEditingStyleSupport)
444 - (DOMCSSStyleDeclaration *)_emptyStyle;
445 - (NSString *)_colorAsString:(NSColor *)color;
448 @interface NSView (WebHTMLViewFileInternal)
449 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
452 @interface NSMutableDictionary (WebHTMLViewFileInternal)
453 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
456 struct WebHTMLViewInterpretKeyEventsParameters {
457 KeyboardEvent* event;
458 bool eventInterpretationHadSideEffects;
459 bool shouldSaveCommands;
461 bool executingSavedKeypressCommands;
464 @interface WebHTMLViewPrivate : NSObject {
467 BOOL ignoringMouseDraggedEvents;
469 BOOL paginateScreenContent;
470 BOOL observingMouseMovedNotifications;
471 BOOL observingSuperviewNotifications;
472 BOOL observingWindowNotifications;
475 BOOL subviewsSetAside;
477 #if USE(ACCELERATED_COMPOSITING)
478 NSView *layerHostingView;
479 BOOL drawingIntoLayer;
482 NSEvent *mouseDownEvent; // Kept after handling the event.
483 BOOL handlingMouseDownEvent;
484 NSEvent *keyDownEvent; // Kept after handling the event.
486 // A WebHTMLView has a single input context, but we return nil when in non-editable content to avoid making input methods do their work.
487 // This state is saved each time selection changes, because computing it causes style recalc, which is not always safe to do.
488 BOOL exposeInputContext;
490 // Track whether the view has set a secure input state.
491 BOOL isInSecureInputState;
493 BOOL _forceUpdateSecureInputState;
495 NSPoint lastScrollPosition;
496 BOOL inScrollPositionChanged;
498 WebPluginController *pluginController;
501 NSToolTipTag lastToolTipTag;
502 id trackingRectOwner;
503 void *trackingRectUserData;
505 NSTimer *autoscrollTimer;
506 NSEvent *autoscrollTriggerEvent;
510 NSMutableDictionary *highlighters;
513 WebTextCompletionController *completionController;
515 BOOL transparentBackground;
517 WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters;
519 WebDataSource *dataSource;
520 WebCore::CachedImage* promisedDragTIFFDataSource;
522 SEL selectorForDoCommandBySelector;
525 BOOL enumeratingSubviews;
531 static NSCellStateValue kit(TriState state)
541 ASSERT_NOT_REACHED();
545 @implementation WebHTMLViewPrivate
549 JSC::initializeThreading();
550 WTF::initializeMainThreadToProcessMainThread();
551 WebCoreObjCFinalizeOnMainThread(self);
553 if (!oldSetCursorForMouseLocationIMP) {
554 Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
555 ASSERT(setCursorMethod);
556 oldSetCursorForMouseLocationIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
557 ASSERT(oldSetCursorForMouseLocationIMP);
560 #if USE(ACCELERATED_COMPOSITING)
561 if (!oldSetNeedsDisplayInRectIMP) {
562 Method setNeedsDisplayInRectMethod = class_getInstanceMethod([NSView class], @selector(setNeedsDisplayInRect:));
563 ASSERT(setNeedsDisplayInRectMethod);
564 oldSetNeedsDisplayInRectIMP = method_setImplementation(setNeedsDisplayInRectMethod, (IMP)setNeedsDisplayInRect);
565 ASSERT(oldSetNeedsDisplayInRectIMP);
567 #endif // USE(ACCELERATED_COMPOSITING)
574 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self))
577 ASSERT(!autoscrollTimer);
578 ASSERT(!autoscrollTriggerEvent);
580 [mouseDownEvent release];
581 [keyDownEvent release];
582 [pluginController release];
584 [completionController release];
585 [dataSource release];
586 [highlighters release];
587 if (promisedDragTIFFDataSource)
588 promisedDragTIFFDataSource->removeClient(promisedDataClient());
595 ASSERT_MAIN_THREAD();
597 if (promisedDragTIFFDataSource)
598 promisedDragTIFFDataSource->removeClient(promisedDataClient());
605 [mouseDownEvent release];
606 [keyDownEvent release];
607 [pluginController release];
609 [completionController release];
610 [dataSource release];
611 [highlighters release];
612 if (promisedDragTIFFDataSource)
613 promisedDragTIFFDataSource->removeClient(promisedDataClient());
615 mouseDownEvent = nil;
617 pluginController = nil;
619 completionController = nil;
622 promisedDragTIFFDataSource = 0;
624 #if USE(ACCELERATED_COMPOSITING)
625 layerHostingView = nil;
631 @implementation WebHTMLView (WebHTMLViewFileInternal)
633 - (DOMRange *)_documentRange
635 return [[[self _frame] DOMDocument] _documentRange];
638 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
640 NSEnumerator *enumerator = [paths objectEnumerator];
643 while ((path = [enumerator nextObject]) != nil) {
644 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
645 if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
652 - (WebDataSource *)_dataSource
654 return _private->dataSource;
657 - (WebView *)_webView
659 return [_private->dataSource _webView];
662 - (WebFrameView *)_frameView
664 return [[_private->dataSource webFrame] frameView];
667 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
669 DOMDocumentFragment *fragment;
670 NSEnumerator *enumerator = [paths objectEnumerator];
671 NSMutableArray *domNodes = [[NSMutableArray alloc] init];
674 while ((path = [enumerator nextObject]) != nil) {
675 // Non-image file types; _web_userVisibleString is appropriate here because this will
676 // be pasted as visible text.
677 NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
678 [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
681 fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes];
685 return [fragment firstChild] != nil ? fragment : nil;
688 + (NSArray *)_excludedElementsForAttributedStringConversion
690 static NSArray *elements = nil;
691 if (elements == nil) {
692 elements = [[NSArray alloc] initWithObjects:
693 // Omit style since we want style to be inline so the fragment can be easily inserted.
695 // Omit xml so the result is not XHTML.
697 // Omit tags that will get stripped when converted to a fragment anyway.
698 @"doctype", @"html", @"head", @"body",
699 // Omit deprecated tags.
700 @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
701 // Omit object so no file attachments are part of the fragment.
708 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
710 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
711 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
713 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
714 CFRelease(UUIDString);
719 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
720 inContext:(DOMRange *)context
721 allowPlainText:(BOOL)allowPlainText
723 NSArray *types = [pasteboard types];
724 DOMDocumentFragment *fragment = nil;
726 if ([types containsObject:WebArchivePboardType] &&
727 (fragment = [self _documentFragmentFromPasteboard:pasteboard
728 forType:WebArchivePboardType
733 if ([types containsObject:NSFilenamesPboardType] &&
734 (fragment = [self _documentFragmentFromPasteboard:pasteboard
735 forType:NSFilenamesPboardType
740 if ([types containsObject:NSHTMLPboardType] &&
741 (fragment = [self _documentFragmentFromPasteboard:pasteboard
742 forType:NSHTMLPboardType
747 if ([types containsObject:NSRTFDPboardType] &&
748 (fragment = [self _documentFragmentFromPasteboard:pasteboard
749 forType:NSRTFDPboardType
754 if ([types containsObject:NSRTFPboardType] &&
755 (fragment = [self _documentFragmentFromPasteboard:pasteboard
756 forType:NSRTFPboardType
761 if ([types containsObject:NSTIFFPboardType] &&
762 (fragment = [self _documentFragmentFromPasteboard:pasteboard
763 forType:NSTIFFPboardType
768 if ([types containsObject:NSPDFPboardType] &&
769 (fragment = [self _documentFragmentFromPasteboard:pasteboard
770 forType:NSPDFPboardType
775 #ifdef BUILDING_ON_LEOPARD
776 if ([types containsObject:NSPICTPboardType] &&
777 (fragment = [self _documentFragmentFromPasteboard:pasteboard
778 forType:NSPICTPboardType
784 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
785 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
786 if ([types containsObject:(NSString*)kUTTypePNG] &&
787 (fragment = [self _documentFragmentFromPasteboard:pasteboard
788 forType:(NSString*)kUTTypePNG
793 if ([types containsObject:NSURLPboardType] &&
794 (fragment = [self _documentFragmentFromPasteboard:pasteboard
795 forType:NSURLPboardType
800 if (allowPlainText && [types containsObject:NSStringPboardType] &&
801 (fragment = [self _documentFragmentFromPasteboard:pasteboard
802 forType:NSStringPboardType
811 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
813 NSArray *types = [pasteboard types];
815 if ([types containsObject:NSStringPboardType])
816 return [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping];
818 NSAttributedString *attributedString = nil;
821 if ([types containsObject:NSRTFDPboardType])
822 attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
823 if (attributedString == nil && [types containsObject:NSRTFPboardType])
824 attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
825 if (attributedString != nil) {
826 string = [[attributedString string] copy];
827 [attributedString release];
828 return [string autorelease];
831 if ([types containsObject:NSFilenamesPboardType]) {
832 string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
839 if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
840 string = [URL _web_userVisibleString];
841 if ([string length] > 0)
848 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
850 WebView *webView = [[self _webView] retain];
851 [webView _setInsertionPasteboard:pasteboard];
853 DOMRange *range = [self _selectedRange];
854 Frame* coreFrame = core([self _frame]);
856 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
857 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
858 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted])
859 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
861 // Mail is ignoring the frament passed to the delegate and creates a new one.
862 // We want to avoid creating the fragment twice.
863 if (applicationIsAppleMail()) {
864 if ([self _shouldInsertFragment:nil replacingDOMRange:range givenAction:WebViewInsertActionPasted]) {
865 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
867 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
870 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
871 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted])
872 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
875 [webView _setInsertionPasteboard:nil];
879 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
881 WebView *webView = [[self _webView] retain];
882 [webView _setInsertionPasteboard:pasteboard];
884 NSString *text = [self _plainTextFromPasteboard:pasteboard];
885 if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
886 [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
888 [webView _setInsertionPasteboard:nil];
892 // This method is needed to support Mac OS X services.
893 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
895 Frame* coreFrame = core([self _frame]);
898 if (coreFrame->selection()->isContentRichlyEditable())
899 [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
901 [self _pasteAsPlainTextWithPasteboard:pasteboard];
905 - (void)_removeMouseMovedObserverUnconditionally
907 if (!_private || !_private->observingMouseMovedNotifications)
910 [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil];
911 _private->observingMouseMovedNotifications = false;
914 - (void)_removeSuperviewObservers
916 if (!_private || !_private->observingSuperviewNotifications)
919 NSView *superview = [self superview];
920 if (!superview || ![self window])
923 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
924 [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview];
925 [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview];
927 _private->observingSuperviewNotifications = false;
930 - (void)_removeWindowObservers
932 if (!_private->observingWindowNotifications)
935 NSWindow *window = [self window];
939 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
940 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
941 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
942 [notificationCenter removeObserver:self name:WKWindowWillOrderOnScreenNotification() object:window];
943 [notificationCenter removeObserver:self name:WKWindowWillOrderOffScreenNotification() object:window];
944 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window];
946 _private->observingWindowNotifications = false;
949 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
951 WebView *webView = [self _webView];
952 DOMNode *child = [fragment firstChild];
953 if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
954 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
955 return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
958 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
960 WebView *webView = [self _webView];
961 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
964 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
966 return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
969 - (DOMRange *)_selectedRange
971 Frame* coreFrame = core([self _frame]);
972 return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil;
975 - (BOOL)_shouldDeleteRange:(DOMRange *)range
977 Frame* coreFrame = core([self _frame]);
978 return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
981 - (NSView *)_hitViewForEvent:(NSEvent *)event
983 // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.
984 // Callers of this method, however, want to query the deepest view instead.
985 forceNSViewHitTest = YES;
986 NSView *hitView = [(NSView *)[[self window] contentView] hitTest:[event locationInWindow]];
987 forceNSViewHitTest = NO;
991 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
993 // Put HTML on the pasteboard.
994 if ([types containsObject:WebArchivePboardType]) {
995 if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) {
996 if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0)
997 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType];
1001 // Put the attributed string on the pasteboard (RTF/RTFD format).
1002 if ([types containsObject:NSRTFDPboardType]) {
1003 if (attributedString == nil) {
1004 attributedString = [self selectedAttributedString];
1006 NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
1007 [pasteboard setData:RTFDData forType:NSRTFDPboardType];
1009 if ([types containsObject:NSRTFPboardType]) {
1010 if (!attributedString)
1011 attributedString = [self selectedAttributedString];
1012 if ([attributedString containsAttachments])
1013 attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
1014 NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
1015 [pasteboard setData:RTFData forType:NSRTFPboardType];
1018 // Put plain string on the pasteboard.
1019 if ([types containsObject:NSStringPboardType]) {
1020 // Map to a plain old space because this is better for source code, other browsers do it,
1021 // and because HTML forces you to do this any time you want two spaces in a row.
1022 NSMutableString *s = [[self selectedString] mutableCopy];
1023 const unichar NonBreakingSpaceCharacter = 0xA0;
1024 NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
1025 [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
1026 [pasteboard setString:s forType:NSStringPboardType];
1030 if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
1031 [pasteboard setData:nil forType:WebSmartPastePboardType];
1035 - (void)_setMouseDownEvent:(NSEvent *)event
1037 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
1039 if (event == _private->mouseDownEvent)
1043 [_private->mouseDownEvent release];
1044 _private->mouseDownEvent = event;
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 originalPageWidth:0 originalPageHeight: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 originalPageWidth:0 originalPageHeight: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 originalPageWidth:0 originalPageHeight: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 originalPageWidth:0 originalPageHeight: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 originalPageWidth:0 originalPageHeight: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 originalPageWidth:0 originalPageHeight: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 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1182 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1183 modifierFlags:[[NSApp currentEvent] modifierFlags]
1184 timestamp:[NSDate timeIntervalSinceReferenceDate]
1185 windowNumber:[[self window] windowNumber]
1186 context:[[NSApp currentEvent] context]
1187 eventNumber:0 clickCount:0 pressure:0];
1189 [self _updateMouseoverWithEvent:fakeEvent];
1192 - (void)_frameOrBoundsChanged
1194 WebView *webView = [self _webView];
1195 WebDynamicScrollBarsView *scrollView = [[[webView mainFrame] frameView] _scrollView];
1197 NSPoint origin = [[self superview] bounds].origin;
1198 if (!NSEqualPoints(_private->lastScrollPosition, origin) && ![scrollView inProgrammaticScroll]) {
1199 if (Frame* coreFrame = core([self _frame])) {
1200 if (FrameView* coreView = coreFrame->view()) {
1201 _private->inScrollPositionChanged = YES;
1202 coreView->scrollPositionChangedViaPlatformWidget();
1203 _private->inScrollPositionChanged = NO;
1207 [_private->completionController endRevertingChange:NO moveLeft:NO];
1209 [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
1211 _private->lastScrollPosition = origin;
1213 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
1214 [self _updateLayerHostingViewPosition];
1218 - (void)_setAsideSubviews
1220 ASSERT(!_private->subviewsSetAside);
1221 ASSERT(_private->savedSubviews == nil);
1222 _private->savedSubviews = _subviews;
1223 #if USE(ACCELERATED_COMPOSITING)
1224 // We need to keep the layer-hosting view in the subviews, otherwise the layers flash.
1225 if (_private->layerHostingView) {
1226 NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil];
1227 _subviews = newSubviews;
1233 _private->subviewsSetAside = YES;
1236 - (void)_restoreSubviews
1238 ASSERT(_private->subviewsSetAside);
1239 #if USE(ACCELERATED_COMPOSITING)
1240 if (_private->layerHostingView) {
1241 [_subviews release];
1242 _subviews = _private->savedSubviews;
1244 ASSERT(_subviews == nil);
1245 _subviews = _private->savedSubviews;
1248 ASSERT(_subviews == nil);
1249 _subviews = _private->savedSubviews;
1251 _private->savedSubviews = nil;
1252 _private->subviewsSetAside = NO;
1257 - (void)didAddSubview:(NSView *)subview
1259 if (_private->enumeratingSubviews)
1260 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]));
1265 - (void)viewWillDraw
1267 // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1268 // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1269 // once the FIXME in _isTopHTMLView is fixed.
1270 if (_private->dataSource && [self _isTopHTMLView])
1271 [self _web_updateLayoutAndStyleIfNeededRecursive];
1272 [super viewWillDraw];
1276 // Don't let AppKit even draw subviews. We take care of that.
1277 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1279 // This helps when we print as part of a larger print process.
1280 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1281 BOOL wasInPrintingMode = _private->printing;
1282 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1284 if (!wasInPrintingMode)
1285 [self _web_setPrintingModeRecursive];
1287 [self _web_updateLayoutAndStyleIfNeededRecursive];
1288 } else if (wasInPrintingMode)
1289 [self _web_clearPrintingModeRecursive];
1291 // There are known cases where -viewWillDraw is not called on all views being drawn.
1292 // See <rdar://problem/6964278> for example. Performing layout at this point prevents us from
1293 // trying to paint without layout (which WebCore now refuses to do, instead bailing out without
1294 // drawing at all), but we may still fail to update any regions dirtied by the layout which are
1295 // not already dirty.
1296 if ([self _needsLayout]) {
1297 NSInteger rectCount;
1298 [self getRectsBeingDrawn:0 count:&rectCount];
1300 LOG_ERROR("View needs layout. Either -viewWillDraw wasn't called or layout was invalidated during the display operation. Performing layout now.");
1301 [self _web_updateLayoutAndStyleIfNeededRecursive];
1305 [self _setAsideSubviews];
1306 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1307 [self _restoreSubviews];
1309 if (wasInPrintingMode != isPrinting) {
1310 if (wasInPrintingMode)
1311 [self _web_setPrintingModeRecursive];
1313 [self _web_clearPrintingModeRecursive];
1317 // Don't let AppKit even draw subviews. We take care of that.
1318 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1320 BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1322 BOOL wasInPrintingMode = _private->printing;
1323 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1325 if (needToSetAsideSubviews) {
1326 // This helps when we print as part of a larger print process.
1327 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1329 if (!wasInPrintingMode)
1330 [self _web_setPrintingModeRecursive];
1332 [self _web_updateLayoutAndStyleIfNeededRecursive];
1333 } else if (wasInPrintingMode)
1334 [self _web_clearPrintingModeRecursive];
1337 [self _setAsideSubviews];
1340 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1342 if (needToSetAsideSubviews) {
1343 if (wasInPrintingMode != isPrinting) {
1344 if (wasInPrintingMode)
1345 [self _web_setPrintingModeRecursive];
1347 [self _web_clearPrintingModeRecursive];
1350 [self _restoreSubviews];
1354 // Don't let AppKit even draw subviews. We take care of that.
1355 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView
1358 [self _setAsideSubviews];
1359 [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView];
1360 [self _restoreSubviews];
1363 - (BOOL)_insideAnotherHTMLView
1365 return self != [self _topHTMLView];
1368 - (NSView *)hitTest:(NSPoint)point
1370 // WebHTMLView objects handle all events for objects inside them.
1371 // To get those events, we prevent hit testing from AppKit.
1373 // But there are three exceptions to this:
1374 // 1) For right mouse clicks and control clicks we don't yet have an implementation
1375 // that works for nested views, so we let the hit testing go through the
1376 // standard NSView code path (needs to be fixed, see bug 4361618).
1377 // 2) Java depends on doing a hit test inside it's mouse moved handling,
1378 // so we let the hit testing go through the standard NSView code path
1379 // when the current event is a mouse move (except when we are calling
1380 // from _updateMouseoverWithEvent, so we have to use a global,
1381 // forceWebHTMLViewHitTest, for that)
1382 // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1383 // both need to figure out which view to check with inside the WebHTMLView.
1384 // They use a global to change the behavior of hitTest: so they can get the
1385 // right view. The global is forceNSViewHitTest and the method they use to
1386 // do the hit testing is _hitViewForEvent:. (But this does not work correctly
1387 // when there is HTML overlapping the view, see bug 4361626)
1388 // 4) NSAccessibilityHitTest relies on this for checking the cursor position.
1389 // Our check for that is whether the event is NSFlagsChanged. This works
1390 // for VoiceOver's Control-Option-F5 command (move focus to item under cursor)
1391 // and Dictionary's Command-Control-D (open dictionary popup for item under cursor).
1392 // This is of course a hack.
1394 if (_private->closed)
1397 BOOL captureHitsOnSubviews;
1398 if (forceNSViewHitTest)
1399 captureHitsOnSubviews = NO;
1400 else if (forceWebHTMLViewHitTest)
1401 captureHitsOnSubviews = YES;
1403 // FIXME: Why doesn't this include mouse entered/exited events, or other mouse button events?
1404 NSEvent *event = [[self window] currentEvent];
1405 captureHitsOnSubviews = !([event type] == NSMouseMoved
1406 || [event type] == NSRightMouseDown
1407 || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1408 || [event type] == NSFlagsChanged);
1411 if (!captureHitsOnSubviews) {
1412 NSView* hitView = [super hitTest:point];
1413 #if USE(ACCELERATED_COMPOSITING)
1414 if (_private && hitView == _private->layerHostingView)
1419 if ([[self superview] mouse:point inRect:[self frame]])
1424 - (void)_clearLastHitViewIfSelf
1426 if (lastHitView == self)
1430 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1432 ASSERT(_private->trackingRectOwner == nil);
1433 _private->trackingRectOwner = owner;
1434 _private->trackingRectUserData = data;
1435 return TRACKING_RECT_TAG;
1438 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1440 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1441 ASSERT(_private->trackingRectOwner == nil);
1442 _private->trackingRectOwner = owner;
1443 _private->trackingRectUserData = data;
1444 return TRACKING_RECT_TAG;
1447 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1450 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1451 ASSERT(_private->trackingRectOwner == nil);
1452 _private->trackingRectOwner = owner;
1453 _private->trackingRectUserData = userDataList[0];
1454 trackingNums[0] = TRACKING_RECT_TAG;
1457 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1462 if (_private && (tag == TRACKING_RECT_TAG)) {
1463 _private->trackingRectOwner = nil;
1467 if (_private && (tag == _private->lastToolTipTag)) {
1468 [super removeTrackingRect:tag];
1469 _private->lastToolTipTag = 0;
1473 // If any other tracking rect is being removed, we don't know how it was created
1474 // and it's possible there's a leak involved (see 3500217)
1475 ASSERT_NOT_REACHED();
1478 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1481 for (i = 0; i < count; ++i) {
1485 ASSERT(tag == TRACKING_RECT_TAG);
1486 if (_private != nil) {
1487 _private->trackingRectOwner = nil;
1492 - (void)_sendToolTipMouseExited
1494 // Nothing matters except window, trackingNumber, and userData.
1495 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1496 location:NSMakePoint(0, 0)
1499 windowNumber:[[self window] windowNumber]
1502 trackingNumber:TRACKING_RECT_TAG
1503 userData:_private->trackingRectUserData];
1504 [_private->trackingRectOwner mouseExited:fakeEvent];
1507 - (void)_sendToolTipMouseEntered
1509 // Nothing matters except window, trackingNumber, and userData.
1510 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1511 location:NSMakePoint(0, 0)
1514 windowNumber:[[self window] windowNumber]
1517 trackingNumber:TRACKING_RECT_TAG
1518 userData:_private->trackingRectUserData];
1519 [_private->trackingRectOwner mouseEntered:fakeEvent];
1522 - (void)_setToolTip:(NSString *)string
1524 NSString *toolTip = [string length] == 0 ? nil : string;
1525 NSString *oldToolTip = _private->toolTip;
1526 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1530 [self _sendToolTipMouseExited];
1531 [oldToolTip release];
1533 _private->toolTip = [toolTip copy];
1535 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1536 [self removeAllToolTips];
1537 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1538 _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1539 [self _sendToolTipMouseEntered];
1543 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1545 return [[_private->toolTip copy] autorelease];
1548 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1550 if (_private->closed)
1553 NSView *contentView = [[event window] contentView];
1554 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1556 forceWebHTMLViewHitTest = YES;
1557 NSView *hitView = [contentView hitTest:locationForHitTest];
1558 forceWebHTMLViewHitTest = NO;
1560 WebHTMLView *view = nil;
1561 if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
1562 view = (WebHTMLView *)hitView;
1567 if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1568 // If we are moving out of a view (or frame), let's pretend the mouse moved
1569 // all the way out of that view. But we have to account for scrolling, because
1570 // WebCore doesn't understand our clipping.
1571 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1572 float yScroll = visibleRect.origin.y;
1573 float xScroll = visibleRect.origin.x;
1575 NSEvent *event = [NSEvent mouseEventWithType:NSMouseMoved
1576 location:NSMakePoint(-1 - xScroll, -1 - yScroll)
1577 modifierFlags:[[NSApp currentEvent] modifierFlags]
1578 timestamp:[NSDate timeIntervalSinceReferenceDate]
1579 windowNumber:[[view window] windowNumber]
1580 context:[[NSApp currentEvent] context]
1581 eventNumber:0 clickCount:0 pressure:0];
1582 if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1583 lastHitCoreFrame->eventHandler()->mouseMoved(event);
1589 if (Frame* coreFrame = core([view _frame]))
1590 coreFrame->eventHandler()->mouseMoved(event);
1596 + (NSArray *)_insertablePasteboardTypes
1598 static NSArray *types = nil;
1600 types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType,
1601 #ifdef BUILDING_ON_LEOPARD
1604 NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
1610 + (NSArray *)_selectionPasteboardTypes
1612 // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1613 return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1616 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1618 [self setPromisedDragTIFFDataSource:0];
1621 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1623 if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1624 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1625 [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1627 } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1628 if (Image* image = [self promisedDragTIFFDataSource]->image())
1629 [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1630 [self setPromisedDragTIFFDataSource:0];
1634 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event
1636 [self autoscroll:event];
1637 [self _startAutoscrollTimer:event];
1640 - (WebPluginController *)_pluginController
1642 return _private->pluginController;
1645 - (void)_layoutForPrinting
1647 // Set printing mode temporarily so we can adjust the size of the view. This will allow
1648 // AppKit's pagination code to use the correct height for the page content. Leaving printing
1649 // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1650 // turn it off again after adjusting the size.
1651 [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1652 [self _web_clearPrintingModeRecursive];
1655 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1657 if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
1659 *beforeString = nil;
1665 [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
1668 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
1670 return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
1673 - (void)_startAutoscrollTimer:(NSEvent *)triggerEvent
1675 if (_private->autoscrollTimer == nil) {
1676 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1677 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1678 _private->autoscrollTriggerEvent = [triggerEvent retain];
1682 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1683 // We can't remove this yet because it's still in use by Mail.
1684 - (NSRect)_selectionRect
1686 return [self selectionRect];
1689 - (void)_stopAutoscrollTimer
1691 NSTimer *timer = _private->autoscrollTimer;
1692 _private->autoscrollTimer = nil;
1693 [_private->autoscrollTriggerEvent release];
1694 _private->autoscrollTriggerEvent = nil;
1701 // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1702 // a mouse up event.
1703 BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);
1705 [self _stopAutoscrollTimer];
1709 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1710 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1711 modifierFlags:[[NSApp currentEvent] modifierFlags]
1712 timestamp:[NSDate timeIntervalSinceReferenceDate]
1713 windowNumber:[[self window] windowNumber]
1714 context:[[NSApp currentEvent] context]
1715 eventNumber:0 clickCount:0 pressure:0];
1716 [self mouseDragged:fakeEvent];
1721 Frame* coreFrame = core([self _frame]);
1722 return coreFrame && coreFrame->editor()->canEdit();
1725 - (BOOL)_canEditRichly
1727 Frame* coreFrame = core([self _frame]);
1728 return coreFrame && coreFrame->editor()->canEditRichly();
1731 - (BOOL)_canAlterCurrentSelection
1733 return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1736 - (BOOL)_hasSelection
1738 Frame* coreFrame = core([self _frame]);
1739 return coreFrame && coreFrame->selection()->isRange();
1742 - (BOOL)_hasSelectionOrInsertionPoint
1744 Frame* coreFrame = core([self _frame]);
1745 return coreFrame && coreFrame->selection()->isCaretOrRange();
1748 - (BOOL)_hasInsertionPoint
1750 Frame* coreFrame = core([self _frame]);
1751 return coreFrame && coreFrame->selection()->isCaret();
1756 Frame* coreFrame = core([self _frame]);
1757 return coreFrame && coreFrame->selection()->isContentEditable();
1760 - (BOOL)_transparentBackground
1762 return _private->transparentBackground;
1765 - (void)_setTransparentBackground:(BOOL)f
1767 _private->transparentBackground = f;
1770 - (NSImage *)_selectionDraggingImage
1772 if (![self _hasSelection])
1774 NSImage *dragImage = core([self _frame])->selectionImage();
1775 [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1779 - (NSRect)_selectionDraggingRect
1781 // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1782 return [self selectionRect];
1785 - (DOMNode *)_insertOrderedList
1787 Frame* coreFrame = core([self _frame]);
1788 return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
1791 - (DOMNode *)_insertUnorderedList
1793 Frame* coreFrame = core([self _frame]);
1794 return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
1797 - (BOOL)_canIncreaseSelectionListLevel
1799 Frame* coreFrame = core([self _frame]);
1800 return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
1803 - (BOOL)_canDecreaseSelectionListLevel
1805 Frame* coreFrame = core([self _frame]);
1806 return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
1809 - (DOMNode *)_increaseSelectionListLevel
1811 Frame* coreFrame = core([self _frame]);
1812 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
1815 - (DOMNode *)_increaseSelectionListLevelOrdered
1817 Frame* coreFrame = core([self _frame]);
1818 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
1821 - (DOMNode *)_increaseSelectionListLevelUnordered
1823 Frame* coreFrame = core([self _frame]);
1824 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
1827 - (void)_decreaseSelectionListLevel
1829 Frame* coreFrame = core([self _frame]);
1831 coreFrame->editor()->decreaseSelectionListLevel();
1834 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1836 if (!_private->highlighters)
1837 _private->highlighters = [[NSMutableDictionary alloc] init];
1838 [_private->highlighters setObject:highlighter forKey:type];
1841 - (void)_removeHighlighterOfType:(NSString*)type
1843 [_private->highlighters removeObjectForKey:type];
1846 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1848 ASSERT([self _hasSelection]);
1849 NSArray *types = [self pasteboardTypesForSelection];
1851 // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1852 NSAttributedString *attributedString = [self selectedAttributedString];
1853 NSMutableArray *mutableTypes = nil;
1854 if (![attributedString containsAttachments]) {
1855 mutableTypes = [types mutableCopy];
1856 [mutableTypes removeObject:NSRTFDPboardType];
1857 types = mutableTypes;
1860 [pasteboard declareTypes:types owner:[self _topHTMLView]];
1861 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1862 [mutableTypes release];
1867 // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing
1868 // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
1869 if (!_private || _private->closed)
1872 _private->closed = YES;
1874 [self _clearLastHitViewIfSelf];
1875 [self _removeMouseMovedObserverUnconditionally];
1876 [self _removeWindowObservers];
1877 [self _removeSuperviewObservers];
1878 [_private->pluginController destroyAllPlugins];
1879 [_private->pluginController setDataSource:nil];
1880 // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1881 [self removeAllToolTips];
1883 if (_private->isInSecureInputState) {
1884 DisableSecureEventInput();
1885 _private->isInSecureInputState = NO;
1891 - (BOOL)_hasHTMLDocument
1893 Frame* coreFrame = core([self _frame]);
1896 Document* document = coreFrame->document();
1897 return document && document->isHTMLDocument();
1900 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1901 forType:(NSString *)pboardType
1902 inContext:(DOMRange *)context
1903 subresources:(NSArray **)subresources
1905 if (pboardType == WebArchivePboardType) {
1906 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1908 *subresources = [archive subresources];
1909 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1913 if (pboardType == NSFilenamesPboardType)
1914 return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1916 if (pboardType == NSHTMLPboardType) {
1917 NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1918 // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1919 if ([HTMLString hasPrefix:@"Version:"]) {
1920 NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
1921 if (range.location != NSNotFound)
1922 HTMLString = [HTMLString substringFromIndex:range.location];
1924 if ([HTMLString length] == 0)
1927 return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
1930 // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
1931 // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
1932 // FIXME: Remove this once bug 5052369 is fixed.
1933 if ([self _hasHTMLDocument] && (pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType)) {
1934 NSAttributedString *string = nil;
1935 if (pboardType == NSRTFDPboardType)
1936 string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
1938 string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
1942 NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
1943 [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
1944 self, @"WebResourceHandler", nil];
1947 BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
1948 if (!wasDeferringCallbacks)
1949 [[self _webView] setDefersCallbacks:YES];
1951 DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length])
1952 document:[[self _frame] DOMDocument]
1953 documentAttributes:documentAttributes
1958 NSEnumerator *e = [s objectEnumerator];
1960 while ((r = [e nextObject]))
1961 [[self _dataSource] addSubresource:r];
1963 if (!wasDeferringCallbacks)
1964 [[self _webView] setDefersCallbacks:NO];
1966 [documentAttributes release];
1970 if (pboardType == NSTIFFPboardType) {
1971 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
1972 URL:uniqueURLWithRelativePart(@"image.tiff")
1973 MIMEType:@"image/tiff"
1974 textEncodingName:nil
1976 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
1980 if (pboardType == NSPDFPboardType) {
1981 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPDFPboardType]
1982 URL:uniqueURLWithRelativePart(@"application.pdf")
1983 MIMEType:@"application/pdf"
1984 textEncodingName:nil
1986 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
1990 #ifdef BUILDING_ON_LEOPARD
1991 if (pboardType == NSPICTPboardType) {
1992 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
1993 URL:uniqueURLWithRelativePart(@"image.pict")
1994 MIMEType:@"image/pict"
1995 textEncodingName:nil
1997 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2002 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
2003 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
2004 if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) {
2005 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG]
2006 URL:uniqueURLWithRelativePart(@"image.png")
2007 MIMEType:@"image/png"
2008 textEncodingName:nil
2010 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2014 if (pboardType == NSURLPboardType) {
2015 NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
2016 DOMDocument* document = [[self _frame] DOMDocument];
2020 DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
2021 NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose.
2022 if ([URLString length] == 0)
2024 NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping];
2025 DOMText *text = [document createTextNode:URLTitleString];
2026 [anchor setHref:URLString];
2027 [anchor appendChild:text];
2028 DOMDocumentFragment *fragment = [document createDocumentFragment];
2029 [fragment appendChild:anchor];
2032 if (pboardType == NSStringPboardType)
2033 return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get());
2037 #if ENABLE(NETSCAPE_PLUGIN_API)
2038 - (void)_pauseNullEventsForAllNetscapePlugins
2040 NSArray *subviews = [self subviews];
2041 unsigned int subviewCount = [subviews count];
2042 unsigned int subviewIndex;
2044 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2045 NSView *subview = [subviews objectAtIndex:subviewIndex];
2046 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2047 [(WebBaseNetscapePluginView *)subview stopTimers];
2052 #if ENABLE(NETSCAPE_PLUGIN_API)
2053 - (void)_resumeNullEventsForAllNetscapePlugins
2055 NSArray *subviews = [self subviews];
2056 unsigned int subviewCount = [subviews count];
2057 unsigned int subviewIndex;
2059 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
2060 NSView *subview = [subviews objectAtIndex:subviewIndex];
2061 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]])
2062 [(WebBaseNetscapePluginView *)subview restartTimers];
2067 - (BOOL)_isUsingAcceleratedCompositing
2069 #if USE(ACCELERATED_COMPOSITING)
2070 return _private->layerHostingView != nil;
2076 - (NSView *)_compositingLayersHostingView
2078 #if USE(ACCELERATED_COMPOSITING)
2079 return _private->layerHostingView;
2085 - (BOOL)_isInPrintMode
2087 return _private->printing;
2090 - (BOOL)_beginPrintModeWithMinimumPageWidth:(CGFloat)minimumPageWidth height:(CGFloat)minimumPageHeight maximumPageWidth:(CGFloat)maximumPageWidth
2092 Frame* frame = core([self _frame]);
2096 if (frame->document() && frame->document()->isFrameSet()) {
2097 minimumPageWidth = 0;
2098 minimumPageHeight = 0;
2101 float maximumShrinkRatio = 0;
2102 if (minimumPageWidth > 0.0)
2103 maximumShrinkRatio = maximumPageWidth / minimumPageWidth;
2105 [self _setPrinting:YES minimumPageLogicalWidth:minimumPageWidth logicalHeight:minimumPageHeight originalPageWidth:minimumPageWidth originalPageHeight:minimumPageHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2109 - (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth height:(float)pageHeight shrinkToFit:(BOOL)shrinkToFit
2111 Frame* frame = core([self _frame]);
2115 Document* document = frame->document();
2116 bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
2118 float pageLogicalWidth = isHorizontal ? pageWidth : pageHeight;
2119 float pageLogicalHeight = isHorizontal ? pageHeight : pageWidth;
2120 FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2121 float maximumShrinkRatio = 1;
2123 // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2124 // according to the page width.
2125 if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2126 minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2127 maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2130 [self _setPrinting:YES minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() originalPageWidth:pageLogicalWidth originalPageHeight:pageLogicalHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2135 - (void)_endPrintMode
2137 [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2140 - (BOOL)_isInScreenPaginationMode
2142 return _private->paginateScreenContent;
2145 - (BOOL)_beginScreenPaginationModeWithPageSize:(CGSize)pageSize shrinkToFit:(BOOL)shrinkToFit
2147 Frame* frame = core([self _frame]);
2151 Document* document = frame->document();
2152 bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
2154 float pageLogicalWidth = isHorizontal ? pageSize.width : pageSize.height;
2155 float pageLogicalHeight = isHorizontal ? pageSize.height : pageSize.width;
2156 FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2157 float maximumShrinkRatio = 1;
2159 // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2160 // according to the page width.
2161 if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2162 minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2163 maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2166 [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() originalPageWidth:pageLogicalWidth originalPageHeight:pageLogicalHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2171 - (void)_endScreenPaginationMode
2173 [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:NO];
2176 - (CGFloat)_adjustedBottomOfPageWithTop:(CGFloat)top bottom:(CGFloat)bottom limit:(CGFloat)bottomLimit
2178 Frame* frame = core([self _frame]);
2182 FrameView* view = frame->view();
2187 view->adjustPageHeightDeprecated(&newBottom, top, bottom, bottomLimit);
2190 // If the new bottom is equal to the old bottom (when both are treated as floats), we just return the original
2191 // bottom. This prevents rounding errors that can occur when converting newBottom to a double.
2192 if (fabs(static_cast<float>(bottom) - newBottom) <= numeric_limits<float>::epsilon())
2201 @implementation NSView (WebHTMLViewFileInternal)
2203 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
2205 unsigned count = [_subviews count];
2206 for (unsigned i = 0; i < count; ++i) {
2207 NSView *child = [_subviews objectAtIndex:i];
2208 if ([child isKindOfClass:[WebHTMLView class]])
2209 [array addObject:child];
2210 [child _web_addDescendantWebHTMLViewsToArray:array];
2216 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
2218 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
2220 if (object == nil) {
2221 [self removeObjectForKey:key];
2223 [self setObject:object forKey:key];
2229 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
2231 NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
2232 return [filename _webkit_hasCaseInsensitiveSuffix:extensionAsSuffix]
2233 || ([extension _webkit_isCaseInsensitiveEqualToString:@"jpeg"]
2234 && [filename _webkit_hasCaseInsensitiveSuffix:@".jpg"]);
2238 @implementation WebHTMLView
2242 [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes]
2243 returnTypes:[[self class] _insertablePasteboardTypes]];
2244 JSC::initializeThreading();
2245 WTF::initializeMainThreadToProcessMainThread();
2246 WebCoreObjCFinalizeOnMainThread(self);
2249 - (id)initWithFrame:(NSRect)frame
2251 self = [super initWithFrame:frame];
2255 [self setFocusRingType:NSFocusRingTypeNone];
2257 // Make all drawing go through us instead of subviews.
2258 [self _setDrawsOwnDescendants:YES];
2260 _private = [[WebHTMLViewPrivate alloc] init];
2262 _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2269 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self))
2272 // We can't assert that close has already been called because
2273 // this view can be removed from it's superview, even though
2274 // it could be needed later, so close if needed.
2283 ASSERT_MAIN_THREAD();
2284 // We can't assert that close has already been called because
2285 // this view can be removed from it's superview, even though
2286 // it could be needed later, so close if needed.
2291 // Returns YES if the delegate returns YES (so we should do no more work).
2292 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2294 BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2295 _private->selectorForDoCommandBySelector = 0;
2296 if (callerAlreadyCalledDelegate)
2298 WebView *webView = [self _webView];
2299 return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2302 typedef HashMap<SEL, String> SelectorNameMap;
2304 // Map selectors into Editor command names.
2305 // This is not needed for any selectors that have the same name as the Editor command.
2306 static const SelectorNameMap* createSelectorExceptionMap()
2308 SelectorNameMap* map = new HashMap<SEL, String>;
2310 map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline");
2311 map->add(@selector(insertParagraphSeparator:), "InsertNewline");
2312 map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab");
2313 map->add(@selector(pageDown:), "MovePageDown");
2314 map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection");
2315 map->add(@selector(pageUp:), "MovePageUp");
2316 map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection");
2321 static String commandNameForSelector(SEL selector)
2323 // Check the exception map first.
2324 static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
2325 SelectorNameMap::const_iterator it = exceptionMap->find(selector);
2326 if (it != exceptionMap->end())
2329 // Remove the trailing colon.
2330 // No need to capitalize the command name since Editor command names are
2331 // not case sensitive.
2332 const char* selectorName = sel_getName(selector);
2333 size_t selectorNameLength = strlen(selectorName);
2334 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2336 return String(selectorName, selectorNameLength - 1);
2339 - (Editor::Command)coreCommandBySelector:(SEL)selector
2341 Frame* coreFrame = core([self _frame]);
2343 return Editor::Command();
2344 return coreFrame->editor()->command(commandNameForSelector(selector));
2347 - (Editor::Command)coreCommandByName:(const char*)name
2349 Frame* coreFrame = core([self _frame]);
2351 return Editor::Command();
2352 return coreFrame->editor()->command(name);
2355 - (void)executeCoreCommandBySelector:(SEL)selector
2357 if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2359 [self coreCommandBySelector:selector].execute();
2362 - (void)executeCoreCommandByName:(const char*)name
2364 [self coreCommandByName:name].execute();
2367 // These commands are forwarded to the Editor object in WebCore.
2368 // Ideally we'd do this for all editing commands; more of the code
2369 // should be moved from here to there, and more commands should be
2370 // added to this list.
2372 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2373 // The functions are identical.
2375 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2377 WEBCORE_COMMAND(alignCenter)
2378 WEBCORE_COMMAND(alignJustified)
2379 WEBCORE_COMMAND(alignLeft)
2380 WEBCORE_COMMAND(alignRight)
2381 WEBCORE_COMMAND(copy)
2382 WEBCORE_COMMAND(cut)
2383 WEBCORE_COMMAND(paste)
2384 WEBCORE_COMMAND(delete)
2385 WEBCORE_COMMAND(deleteBackward)
2386 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2387 WEBCORE_COMMAND(deleteForward)
2388 WEBCORE_COMMAND(deleteToBeginningOfLine)
2389 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2390 WEBCORE_COMMAND(deleteToEndOfLine)
2391 WEBCORE_COMMAND(deleteToEndOfParagraph)
2392 WEBCORE_COMMAND(deleteToMark)
2393 WEBCORE_COMMAND(deleteWordBackward)
2394 WEBCORE_COMMAND(deleteWordForward)
2395 WEBCORE_COMMAND(ignoreSpelling)
2396 WEBCORE_COMMAND(indent)
2397 WEBCORE_COMMAND(insertBacktab)
2398 WEBCORE_COMMAND(insertLineBreak)
2399 WEBCORE_COMMAND(insertNewline)
2400 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2401 WEBCORE_COMMAND(insertParagraphSeparator)
2402 WEBCORE_COMMAND(insertTab)
2403 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2404 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
2405 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
2406 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
2407 WEBCORE_COMMAND(moveBackward)
2408 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2409 WEBCORE_COMMAND(moveDown)
2410 WEBCORE_COMMAND(moveDownAndModifySelection)
2411 WEBCORE_COMMAND(moveForward)
2412 WEBCORE_COMMAND(moveForwardAndModifySelection)
2413 WEBCORE_COMMAND(moveLeft)
2414 WEBCORE_COMMAND(moveLeftAndModifySelection)
2415 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2416 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2417 WEBCORE_COMMAND(moveRight)
2418 WEBCORE_COMMAND(moveRightAndModifySelection)
2419 WEBCORE_COMMAND(moveToBeginningOfDocument)
2420 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2421 WEBCORE_COMMAND(moveToBeginningOfLine)
2422 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2423 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2424 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2425 WEBCORE_COMMAND(moveToBeginningOfSentence)
2426 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2427 WEBCORE_COMMAND(moveToEndOfDocument)
2428 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2429 WEBCORE_COMMAND(moveToEndOfLine)
2430 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2431 WEBCORE_COMMAND(moveToEndOfParagraph)
2432 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2433 WEBCORE_COMMAND(moveToEndOfSentence)
2434 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2435 WEBCORE_COMMAND(moveToLeftEndOfLine)
2436 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
2437 WEBCORE_COMMAND(moveToRightEndOfLine)
2438 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
2439 WEBCORE_COMMAND(moveUp)
2440 WEBCORE_COMMAND(moveUpAndModifySelection)
2441 WEBCORE_COMMAND(moveWordBackward)
2442 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2443 WEBCORE_COMMAND(moveWordForward)
2444 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2445 WEBCORE_COMMAND(moveWordLeft)
2446 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2447 WEBCORE_COMMAND(moveWordRight)
2448 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2449 WEBCORE_COMMAND(outdent)
2450 WEBCORE_COMMAND(pageDown)
2451 WEBCORE_COMMAND(pageDownAndModifySelection)
2452 WEBCORE_COMMAND(pageUp)
2453 WEBCORE_COMMAND(pageUpAndModifySelection)
2454 WEBCORE_COMMAND(pasteAsPlainText)
2455 WEBCORE_COMMAND(selectAll)
2456 WEBCORE_COMMAND(selectLine)
2457 WEBCORE_COMMAND(selectParagraph)
2458 WEBCORE_COMMAND(selectSentence)
2459 WEBCORE_COMMAND(selectToMark)
2460 WEBCORE_COMMAND(selectWord)
2461 WEBCORE_COMMAND(setMark)
2462 WEBCORE_COMMAND(subscript)
2463 WEBCORE_COMMAND(superscript)
2464 WEBCORE_COMMAND(swapWithMark)
2465 WEBCORE_COMMAND(transpose)
2466 WEBCORE_COMMAND(underline)
2467 WEBCORE_COMMAND(unscript)
2468 WEBCORE_COMMAND(yank)
2469 WEBCORE_COMMAND(yankAndSelect)
2471 #undef WEBCORE_COMMAND
2473 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2475 - (IBAction)takeFindStringFromSelection:(id)sender
2479 if (![self _hasSelection]) {
2484 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2487 // This method is needed to support Mac OS X services.
2488 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2490 [pasteboard declareTypes:types owner:[self _topHTMLView]];
2491 [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2495 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2497 BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]);
2498 BOOL isReturnTypeOK = NO;
2500 isReturnTypeOK = YES;
2501 else if ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2502 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts.
2503 isReturnTypeOK = [returnType isEqualToString:NSStringPboardType] || [self _canEditRichly];
2505 if (isSendTypeOK && isReturnTypeOK)
2507 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2510 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2511 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2512 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2513 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2514 // might be using the selector, and we don't want to break them.
2515 - (void)jumpToSelection:(id)sender
2519 if (Frame* coreFrame = core([self _frame]))
2520 coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways);
2523 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2525 SEL action = [item action];
2526 RefPtr<Frame> frame = core([self _frame]);
2531 if (Document* doc = frame->document()) {
2532 if (doc->isPluginDocument())
2534 if (doc->isImageDocument()) {
2535 if (action == @selector(copy:))
2536 return frame->loader()->isComplete();
2541 if (action == @selector(changeSpelling:)
2542 || action == @selector(_changeSpellingFromMenu:)
2543 || action == @selector(checkSpelling:)
2544 || action == @selector(complete:)
2545 || action == @selector(pasteFont:))
2546 return [self _canEdit];
2548 if (action == @selector(showGuessPanel:)) {
2549 // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2550 NSMenuItem *menuItem = (NSMenuItem *)item;
2551 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2552 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2553 [menuItem setTitle:panelShowing
2554 ? UI_STRING_INTERNAL("Hide Spelling and Grammar", "menu item title")
2555 : UI_STRING_INTERNAL("Show Spelling and Grammar", "menu item title")];
2557 return [self _canEdit];
2560 if (action == @selector(changeBaseWritingDirection:)
2561 || action == @selector(makeBaseWritingDirectionLeftToRight:)
2562 || action == @selector(makeBaseWritingDirectionRightToLeft:)) {
2563 NSWritingDirection writingDirection;
2565 if (action == @selector(changeBaseWritingDirection:)) {
2566 writingDirection = static_cast<NSWritingDirection>([item tag]);
2567 if (writingDirection == NSWritingDirectionNatural)
2569 } else if (action == @selector(makeBaseWritingDirectionLeftToRight:))
2570 writingDirection = NSWritingDirectionLeftToRight;
2572 writingDirection = NSWritingDirectionRightToLeft;
2574 NSMenuItem *menuItem = (NSMenuItem *)item;
2575 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2576 String direction = writingDirection == NSWritingDirectionLeftToRight ? "ltr" : "rtl";
2577 [menuItem setState:frame->editor()->selectionHasStyle(CSSPropertyDirection, direction)];
2579 return [self _canEdit];
2582 if (action == @selector(makeBaseWritingDirectionNatural:)) {
2583 NSMenuItem *menuItem = (NSMenuItem *)item;
2584 if ([menuItem isKindOfClass:[NSMenuItem class]])
2585 [menuItem setState:NSOffState];
2589 if (action == @selector(toggleBaseWritingDirection:)) {
2590 NSMenuItem *menuItem = (NSMenuItem *)item;
2591 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2592 // Take control of the title of the menu item instead of just checking/unchecking it because
2593 // a check would be ambiguous.
2594 [menuItem setTitle:frame->editor()->selectionHasStyle(CSSPropertyDirection, "rtl")
2595 ? UI_STRING_INTERNAL("Left to Right", "Left to Right context menu item")
2596 : UI_STRING_INTERNAL("Right to Left", "Right to Left context menu item")];
2598 return [self _canEdit];
2601 if (action == @selector(changeAttributes:)
2602 || action == @selector(changeColor:)
2603 || action == @selector(changeFont:))
2604 return [self _canEditRichly];
2606 if (action == @selector(capitalizeWord:)
2607 || action == @selector(lowercaseWord:)
2608 || action == @selector(uppercaseWord:))
2609 return [self _hasSelection] && [self _isEditable];
2611 if (action == @selector(centerSelectionInVisibleArea:)
2612 || action == @selector(jumpToSelection:)
2613 || action == @selector(copyFont:))
2614 return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2616 if (action == @selector(changeDocumentBackgroundColor:))
2617 return [[self _webView] isEditable] && [self _canEditRichly];
2619 if (action == @selector(_ignoreSpellingFromMenu:)
2620 || action == @selector(_learnSpellingFromMenu:)
2621 || action == @selector(takeFindStringFromSelection:))
2622 return [self _hasSelection];
2624 if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2625 return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste());
2627 if (action == @selector(pasteAsRichText:))
2628 return frame && (frame->editor()->canDHTMLPaste()
2629 || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable()));
2631 if (action == @selector(performFindPanelAction:))
2634 if (action == @selector(_lookUpInDictionaryFromMenu:))
2635 return [self _hasSelection];
2637 if (action == @selector(stopSpeaking:))
2638 return [NSApp isSpeaking];
2640 if (action == @selector(toggleGrammarChecking:)) {
2641 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate
2642 // the selector here because we implement it here, and we must implement it here because the AppKit
2643 // code checks the first responder.
2644 NSMenuItem *menuItem = (NSMenuItem *)item;
2645 if ([menuItem isKindOfClass:[NSMenuItem class]])
2646 [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2650 #ifndef BUILDING_ON_LEOPARD
2651 if (action == @selector(orderFrontSubstitutionsPanel:)) {
2652 NSMenuItem *menuItem = (NSMenuItem *)item;
2653 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2654 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
2655 [menuItem setTitle:panelShowing
2656 ? UI_STRING_INTERNAL("Hide Substitutions", "menu item title")
2657 : UI_STRING_INTERNAL("Show Substitutions", "menu item title")];
2659 return [self _canEdit];
2661 // FIXME 4799134: WebView is the bottleneck for this 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 if (action == @selector(toggleSmartInsertDelete:)) {
2665 NSMenuItem *menuItem = (NSMenuItem *)item;
2666 if ([menuItem isKindOfClass:[NSMenuItem class]])
2667 [menuItem setState:[self smartInsertDeleteEnabled] ? NSOnState : NSOffState];
2668 return [self _canEdit];
2670 if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
2671 NSMenuItem *menuItem = (NSMenuItem *)item;
2672 if ([menuItem isKindOfClass:[NSMenuItem class]])
2673 [menuItem setState:[self isAutomaticQuoteSubstitutionEnabled] ? NSOnState : NSOffState];
2674 return [self _canEdit];
2676 if (action == @selector(toggleAutomaticLinkDetection:)) {
2677 NSMenuItem *menuItem = (NSMenuItem *)item;
2678 if ([menuItem isKindOfClass:[NSMenuItem class]])
2679 [menuItem setState:[self isAutomaticLinkDetectionEnabled] ? NSOnState : NSOffState];
2680 return [self _canEdit];
2682 if (action == @selector(toggleAutomaticDashSubstitution:)) {
2683 NSMenuItem *menuItem = (NSMenuItem *)item;
2684 if ([menuItem isKindOfClass:[NSMenuItem class]])
2685 [menuItem setState:[self isAutomaticDashSubstitutionEnabled] ? NSOnState : NSOffState];
2686 return [self _canEdit];
2688 if (action == @selector(toggleAutomaticTextReplacement:)) {
2689 NSMenuItem *menuItem = (NSMenuItem *)item;
2690 if ([menuItem isKindOfClass:[NSMenuItem class]])
2691 [menuItem setState:[self isAutomaticTextReplacementEnabled] ? NSOnState : NSOffState];
2692 return [self _canEdit];
2694 if (action == @selector(toggleAutomaticSpellingCorrection:)) {
2695 NSMenuItem *menuItem = (NSMenuItem *)item;
2696 if ([menuItem isKindOfClass:[NSMenuItem class]])
2697 [menuItem setState:[self isAutomaticSpellingCorrectionEnabled] ? NSOnState : NSOffState];
2698 return [self _canEdit];
2702 Editor::Command command = [self coreCommandBySelector:action];
2703 if (command.isSupported()) {
2704 NSMenuItem *menuItem = (NSMenuItem *)item;
2705 if ([menuItem isKindOfClass:[NSMenuItem class]])
2706 [menuItem setState:kit(command.state())];
2707 return command.isEnabled();
2713 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2715 // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
2716 // assumes the WebVIew is non-nil.
2717 if (![self _webView])
2719 BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2720 return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2723 - (BOOL)acceptsFirstResponder
2725 // Don't accept first responder when we first click on this view.
2726 // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2727 // Do accept first responder at any other time, for example from keyboard events,
2728 // or from calls back from WebCore once we begin mouse-down event handling.
2729 NSEvent *event = [NSApp currentEvent];
2730 if ([event type] == NSLeftMouseDown
2731 && !_private->handlingMouseDownEvent
2732 && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2738 - (BOOL)maintainsInactiveSelection
2740 // This method helps to determine whether the WebHTMLView should maintain
2741 // an inactive selection when it's not first responder.
2742 // Traditionally, these views have not maintained such selections,
2743 // clearing them when the view was not first responder. However,
2744 // to fix bugs like this one:
2745 // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even
2746 // when they're not firstResponder"
2747 // it was decided to add a switch to act more like an NSTextView.
2749 if ([[self _webView] maintainsInactiveSelection])
2752 // Predict the case where we are losing first responder status only to
2753 // gain it back again. Want to keep the selection in that case.
2754 id nextResponder = [[self window] _newFirstResponderAfterResigning];
2755 if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2756 id contentView = [nextResponder contentView];
2758 nextResponder = contentView;
2760 if ([nextResponder isKindOfClass:[NSClipView class]]) {
2761 id documentView = [nextResponder documentView];
2763 nextResponder = documentView;
2765 if (nextResponder == self)
2768 Frame* coreFrame = core([self _frame]);
2769 bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable();
2770 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2771 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2773 return selectionIsEditable && nextResponderIsInWebView;
2776 - (void)addMouseMovedObserver
2778 if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications)
2781 // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2782 if (!([[self window] isKeyWindow]
2783 #if ENABLE(DASHBOARD_SUPPORT)
2784 || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]
2789 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2790 name:WKMouseMovedNotification() object:nil];
2791 [self _frameOrBoundsChanged];
2792 _private->observingMouseMovedNotifications = true;
2795 - (void)removeMouseMovedObserver
2797 #if ENABLE(DASHBOARD_SUPPORT)
2798 // Don't remove the observer if we're running the Dashboard.
2799 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2803 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2804 [self _removeMouseMovedObserverUnconditionally];
2807 - (void)addSuperviewObservers
2809 if (_private->observingSuperviewNotifications)
2812 NSView *superview = [self superview];
2813 if (!superview || ![self window])
2816 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2817 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview];
2818 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview];
2820 // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2821 // It will check the current scroll against the previous layout's scroll. We need to
2822 // do this here to catch the case where the WebView is laid out at one size, removed from its
2823 // window, resized, and inserted into another window. Our frame/bounds changed notifications
2824 // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2825 [self _frameOrBoundsChanged];
2827 _private->observingSuperviewNotifications = true;
2830 - (void)addWindowObservers
2832 if (_private->observingWindowNotifications)
2835 NSWindow *window = [self window];
2839 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2840 [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
2841 [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
2842 [notificationCenter addObserver:self selector:@selector(windowWillOrderOnScreen:) name:WKWindowWillOrderOnScreenNotification() object:window];
2843 [notificationCenter addObserver:self selector:@selector(windowWillOrderOffScreen:) name:WKWindowWillOrderOffScreenNotification() object:window];
2844 [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
2846 _private->observingWindowNotifications = true;
2849 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2851 [self _removeSuperviewObservers];
2854 - (void)viewDidMoveToSuperview
2856 if ([self superview] != nil)
2857 [self addSuperviewObservers];
2859 #if USE(ACCELERATED_COMPOSITING)
2860 if ([self superview] && [self _isUsingAcceleratedCompositing]) {
2861 WebView *webView = [self _webView];
2862 if ([webView _postsAcceleratedCompositingNotifications])
2863 [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:webView userInfo:nil];
2868 - (void)viewWillMoveToWindow:(NSWindow *)window
2870 // Don't do anything if we aren't initialized. This happens
2871 // when decoding a WebView. When WebViews are decoded their subviews
2872 // are created by initWithCoder: and so won't be normally
2873 // initialized. The stub views are discarded by WebView.
2877 // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2878 [self _removeMouseMovedObserverUnconditionally];
2879 [self _removeWindowObservers];
2880 [self _removeSuperviewObservers];
2882 // FIXME: This accomplishes the same thing as the call to setCanStartMedia(false) in
2883 // WebView. It would be nice to have a single mechanism instead of two.
2884 [[self _pluginController] stopAllPlugins];
2887 - (void)viewDidMoveToWindow
2889 // Don't do anything if we aren't initialized. This happens
2890 // when decoding a WebView. When WebViews are decoded their subviews
2891 // are created by initWithCoder: and so won't be normally
2892 // initialized. The stub views are discarded by WebView.
2893 if (!_private || _private->closed)
2896 [self _stopAutoscrollTimer];
2897 if ([self window]) {
2898 _private->lastScrollPosition = [[self superview] bounds].origin;
2899 [self addWindowObservers];
2900 [self addSuperviewObservers];
2901 [self addMouseMovedObserver];
2903 // FIXME: This accomplishes the same thing as the call to setCanStartMedia(true) in
2904 // WebView. It would be nice to have a single mechanism instead of two.
2905 [[self _pluginController] startAllPlugins];
2907 _private->lastScrollPosition = NSZeroPoint;
2911 - (void)_web_makePluginSubviewsPerformSelector:(SEL)selector withObject:(id)object
2913 #if ENABLE(NETSCAPE_PLUGIN_API)
2914 // Copy subviews because [self subviews] returns the view's mutable internal array,
2915 // and we must avoid mutating the array while enumerating it.
2916 NSArray *subviews = [[self subviews] copy];
2918 NSEnumerator *enumerator = [subviews objectEnumerator];
2919 WebNetscapePluginView *view;
2920 while ((view = [enumerator nextObject]) != nil)
2921 if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
2922 [view performSelector:selector withObject:object];
2928 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2930 [self _web_makePluginSubviewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2933 - (void)viewDidMoveToHostWindow
2935 [self _web_makePluginSubviewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2939 - (void)addSubview:(NSView *)view
2941 [super addSubview:view];
2943 if ([WebPluginController isPlugInView:view])
2944 [[self _pluginController] addPlugin:view];
2947 - (void)willRemoveSubview:(NSView *)subview
2950 // Have to null-check _private, since this can be called via -dealloc when
2951 // cleaning up the the layerHostingView.
2952 if (_private && _private->enumeratingSubviews)
2953 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]));
2956 if ([WebPluginController isPlugInView:subview])
2957 [[self _pluginController] destroyPlugin:subview];
2959 [super willRemoveSubview:subview];
2962 - (void)reapplyStyles
2965 double start = CFAbsoluteTimeGetCurrent();
2968 if (Frame* coreFrame = core([self _frame]))
2969 coreFrame->document()->styleSelectorChanged(RecalcStyleImmediately);
2972 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2973 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2977 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2978 // minPageWidth==0 implies a non-printing layout
2979 - (void)layoutToMinimumPageWidth:(float)minPageLogicalWidth height:(float)minPageLogicalHeight originalPageWidth:(float)originalPageWidth originalPageHeight:(float)originalPageHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustingViewSize:(BOOL)adjustViewSize
2981 if (![self _needsLayout])
2985 double start = CFAbsoluteTimeGetCurrent();
2988 LOG(View, "%@ doing layout", self);
2990 Frame* coreFrame = core([self _frame]);
2994 if (FrameView* coreView = coreFrame->view()) {
2995 if (minPageLogicalWidth > 0.0) {
2996 FloatSize pageSize(minPageLogicalWidth, minPageLogicalHeight);
2997 FloatSize originalPageSize(originalPageWidth, originalPageHeight);
2998 if (coreFrame->document() && coreFrame->document()->renderView() && !coreFrame->document()->renderView()->style()->isHorizontalWritingMode()) {
2999 pageSize = FloatSize(minPageLogicalHeight, minPageLogicalWidth);
3000 originalPageSize = FloatSize(originalPageHeight, originalPageWidth);
3002 coreView->forceLayoutForPagination(pageSize, originalPageSize, maximumShrinkRatio, adjustViewSize ? AdjustViewSize : DoNotAdjustViewSize);
3004 coreView->forceLayout(!adjustViewSize);
3006 coreView->adjustViewSize();
3011 double thisTime = CFAbsoluteTimeGetCurrent() - start;
3012 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
3018 [self layoutToMinimumPageWidth:0 height:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustingViewSize:NO];
3021 // Deliver mouseup events to the DOM for button 2.
3022 - (void)rightMouseUp:(NSEvent *)event
3024 // There's a chance that if we run a nested event loop the event will be released.
3025 // Retaining and then autoreleasing prevents that from causing a problem later here or
3026 // inside AppKit code.
3027 [[event retain] autorelease];
3029 [super rightMouseUp:event];
3031 if (Frame* coreframe = core([self _frame]))
3032 coreframe->eventHandler()->mouseUp(event);
3035 static void setMenuItemTarget(NSMenuItem* menuItem)
3037 // Don't set the menu item's action to the context menu action forwarder if we already
3039 if ([menuItem action])
3042 [menuItem setTarget:[WebMenuTarget sharedMenuTarget]];
3043 [menuItem setAction:@selector(forwardContextMenuAction:)];
3046 static void setMenuTargets(NSMenu* menu)
3048 NSInteger itemCount = [menu numberOfItems];
3049 for (NSInteger i = 0; i < itemCount; ++i) {
3050 NSMenuItem *item = [menu itemAtIndex:i];
3051 setMenuItemTarget(item);
3052 if ([item hasSubmenu])
3053 setMenuTargets([item submenu]);
3057 - (NSMenu *)menuForEvent:(NSEvent *)event
3059 // There's a chance that if we run a nested event loop the event will be released.
3060 // Retaining and then autoreleasing prevents that from causing a problem later here or
3061 // inside AppKit code.
3062 [[event retain] autorelease];
3064 [_private->completionController endRevertingChange:NO moveLeft:NO];
3066 RefPtr<Frame> coreFrame = core([self _frame]);
3070 Page* page = coreFrame->page();
3074 // Match behavior of other browsers by sending a mousedown event for right clicks.
3075 _private->handlingMouseDownEvent = YES;
3076 page->contextMenuController()->clearContextMenu();
3077 coreFrame->eventHandler()->mouseDown(event);
3078 BOOL handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformEventFactory::createPlatformMouseEvent(event, page->chrome()->platformPageClient()));
3079 _private->handlingMouseDownEvent = NO;
3084 // Re-get page, since it might have gone away during event handling.
3085 page = coreFrame->page();
3089 ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
3093 NSArray* menuItems = coreMenu->platformDescription();
3097 NSUInteger count = [menuItems count];
3101 NSMenu* menu = [[[NSMenu alloc] init] autorelease];
3102 for (NSUInteger i = 0; i < count; i++)
3103 [menu addItem:[menuItems objectAtIndex:i]];
3104 setMenuTargets(menu);
3106 [[WebMenuTarget sharedMenuTarget] setMenuController:page->contextMenuController()];
3111 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
3113 return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
3118 Frame* coreFrame = core([self _frame]);
3121 Document* document = coreFrame->document();
3125 document->setFocusedNode(0);
3130 return [[self _webView] drawsBackground];
3134 - (void)setNeedsDisplay:(BOOL)flag
3136 LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO");
3137 [super setNeedsDisplay:flag];
3141 - (void)setNeedsDisplayInRect:(NSRect)invalidRect
3143 if (_private->inScrollPositionChanged) {
3144 // When scrolling, the dirty regions are adjusted for the scroll only
3145 // after NSViewBoundsDidChangeNotification is sent. Translate the invalid
3146 // rect to pre-scrolled coordinates in order to get the right dirty region
3147 // after adjustment. See <rdar://problem/7678927>.
3148 NSPoint origin = [[self superview] bounds].origin;
3149 invalidRect.origin.x -= _private->lastScrollPosition.x - origin.x;
3150 invalidRect.origin.y -= _private->lastScrollPosition.y - origin.y;
3152 [super setNeedsDisplayInRect:invalidRect];
3155 - (void)setNeedsLayout: (BOOL)flag
3157 LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO");
3159 return; // There's no way to say you don't need a layout.
3160 if (Frame* frame = core([self _frame])) {
3161 if (frame->document() && frame->document()->inPageCache())
3163 if (FrameView* view = frame->view())
3164 view->setNeedsLayout();
3168 - (void)setNeedsToApplyStyles: (BOOL)flag
3170 LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO");
3172 return; // There's no way to say you don't need a style recalc.
3173 if (Frame* frame = core([self _frame])) {
3174 if (frame->document() && frame->document()->inPageCache())
3176 frame->document()->scheduleForcedStyleRecalc();
3180 - (void)drawSingleRect:(NSRect)rect
3182 [NSGraphicsContext saveGraphicsState];
3185 ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
3187 [(WebClipView *)[self superview] setAdditionalClip:rect];
3190 if ([self _transparentBackground]) {
3191 [[NSColor clearColor] set];
3195 [[self _frame] _drawRect:rect contentsOnly:YES];
3197 WebView *webView = [self _webView];
3199 // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
3200 // called after the WebView has closed. If the client did not properly close the WebView and set the
3201 // UIDelegate to nil, then the UIDelegate will be stale and this code will crash.
3202 static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
3203 if (version3OrLaterClient)
3204 [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
3206 if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight])
3207 [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]];
3209 [(WebClipView *)[self superview] resetAdditionalClip];
3211 [NSGraphicsContext restoreGraphicsState];
3212 } @catch (NSException *localException) {
3213 [(WebClipView *)[self superview] resetAdditionalClip];
3214 [NSGraphicsContext restoreGraphicsState];
3215 LOG_ERROR("Exception caught while drawing: %@", localException);
3216 [localException raise];
3220 - (void)drawRect:(NSRect)rect
3222 ASSERT_MAIN_THREAD();
3223 LOG(View, "%@ drawing", self);
3225 const NSRect *rects;
3227 [self getRectsBeingDrawn:&rects count:&count];
3229 BOOL subviewsWereSetAside = _private->subviewsSetAside;
3230 if (subviewsWereSetAside)
3231 [self _restoreSubviews];
3234 double start = CFAbsoluteTimeGetCurrent();
3237 // If count == 0 here, use the rect passed in for drawing. This is a workaround for:
3238 // <rdar://problem/3908282> REGRESSION (Mail): No drag image dragging selected text in Blot and Mail
3239 // The reason for the workaround is that this method is called explicitly from the code
3240 // to generate a drag image, and at that time, getRectsBeingDrawn:count: will return a zero count.
3241 const int cRectThreshold = 10;
3242 const float cWastedSpaceThreshold = 0.75f;
3243 BOOL useUnionedRect = (count <= 1) || (count > cRectThreshold);
3244 if (!useUnionedRect) {
3245 // Attempt to guess whether or not we should use the unioned rect or the individual rects.
3246 // We do this by computing the percentage of "wasted space" in the union. If that wasted space
3247 // is too large, then we will do individual rect painting instead.
3248 float unionPixels = (rect.size.width * rect.size.height);
3249 float singlePixels = 0;
3250 for (int i = 0; i < count; ++i)
3251 singlePixels += rects[i].size.width * rects[i].size.height;
3252 float wastedSpace = 1 - (singlePixels / unionPixels);
3253 if (wastedSpace <= cWastedSpaceThreshold)
3254 useUnionedRect = YES;
3258 [self drawSingleRect:rect];
3260 for (int i = 0; i < count; ++i)
3261 [self drawSingleRect:rects[i]];
3265 double thisTime = CFAbsoluteTimeGetCurrent() - start;
3266 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
3269 if (subviewsWereSetAside)
3270 [self _setAsideSubviews];
3272 WebView *webView = [self _webView];
3274 #if USE(ACCELERATED_COMPOSITING)
3275 // Only do the synchronization dance if we're drawing into the window, otherwise
3276 // we risk disabling screen updates when no flush is pending.
3277 if ([NSGraphicsContext currentContext] == [[self window] graphicsContext] && [webView _needsOneShotDrawingSynchronization]) {
3278 // Disable screen updates to minimize the chances of the race between the CA
3279 // display link and AppKit drawing causing flashes.
3280 [[self window] disableScreenUpdatesUntilFlush];
3282 // Make sure any layer changes that happened as a result of layout
3283 // via -viewWillDraw are committed.
3284 [CATransaction flush];
3285 [webView _setNeedsOneShotDrawingSynchronization:NO];
3290 CallUIDelegate(webView, @selector(webView:didDrawFrame:), [self _frame]);
3293 // Turn off the additional clip while computing our visibleRect.
3294 - (NSRect)visibleRect
3296 if (!([[self superview] isKindOfClass:[WebClipView class]]))
3297 return [super visibleRect];
3299 WebClipView *clipView = (WebClipView *)[self superview];
3301 BOOL hasAdditionalClip = [clipView hasAdditionalClip];
3302 if (!hasAdditionalClip) {
3303 return [super visibleRect];
3306 NSRect additionalClip = [clipView additionalClip];
3307 [clipView resetAdditionalClip];
3308 NSRect visibleRect = [super visibleRect];
3309 [clipView setAdditionalClip:additionalClip];
3313 - (void)_invalidateGStatesForTree
3315 // AppKit is in the process of traversing the NSView tree, and is going to send -renewGState to
3316 // descendants, including plug-in views. This can result in calls out to plug-in code and back into
3317 // WebCore via JavaScript, which could normally mutate the NSView tree while it is being traversed.
3318 // Defer those mutations while descendants are being traveresed.
3319 RenderWidget::suspendWidgetHierarchyUpdates();
3320 [super _invalidateGStatesForTree];
3321 RenderWidget::resumeWidgetHierarchyUpdates();
3329 - (void)windowDidBecomeKey:(NSNotification *)notification
3331 if (!pthread_main_np()) {
3332 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3336 NSWindow *keyWindow = [notification object];
3338 if (keyWindow == [self window]) {
3339 [self addMouseMovedObserver];
3340 [self _updateSecureInputState];
3344 - (void)windowDidResignKey:(NSNotification *)notification
3346 if (!pthread_main_np()) {
3347 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3351 NSWindow *formerKeyWindow = [notification object];
3353 if (formerKeyWindow == [self window])
3354 [self removeMouseMovedObserver];
3356 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
3357 [self _updateSecureInputState];
3358 [_private->completionController endRevertingChange:NO moveLeft:NO];
3362 - (void)windowWillOrderOnScreen:(NSNotification *)notification
3364 if (!pthread_main_np()) {
3365 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3369 // Check if the window is already a key window, which can be the case for NSPopovers.
3370 if ([[self window] isKeyWindow])
3371 [self addMouseMovedObserver];
3374 - (void)windowWillOrderOffScreen:(NSNotification *)notification
3376 if (!pthread_main_np()) {
3377 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3381 // When the WebView is in a NSPopover the NSWindowDidResignKeyNotification isn't sent
3382 // unless the parent window loses key. So we need to remove the mouse moved observer.
3383 [self removeMouseMovedObserver];
3386 - (void)windowWillClose:(NSNotification *)notification
3388 if (!pthread_main_np()) {
3389 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3393 [_private->completionController endRevertingChange:NO moveLeft:NO];
3394 [[self _pluginController] destroyAllPlugins];
3397 - (void)scrollWheel:(NSEvent *)event
3399 // There's a chance that responding to this event will run a nested event loop, and
3400 // fetching a new event might release the old one. Retaining and then autoreleasing
3401 // the current event prevents that from causing a problem inside WebKit or AppKit code.
3402 [[event retain] autorelease];
3404 Frame* frame = core([self _frame]);
3405 if (!frame || !frame->eventHandler()->wheelEvent(event))
3406 [super scrollWheel:event];
3409 - (BOOL)_isSelectionEvent:(NSEvent *)event
3411 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3412 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
3415 - (BOOL)_isScrollBarEvent:(NSEvent *)event
3417 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3418 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsInScrollBarKey] boolValue];
3421 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3423 // There's a chance that responding to this event will run a nested event loop, and
3424 // fetching a new event might release the old one. Retaining and then autoreleasing
3425 // the current event prevents that from causing a problem inside WebKit or AppKit code.
3426 [[event retain] autorelease];
3428 NSView *hitView = [self _hitViewForEvent:event];
3429 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3431 #if ENABLE(DASHBOARD_SUPPORT)
3432 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
3437 bool result = false;
3438 if (Frame* coreFrame = core([hitHTMLView _frame])) {
3439 coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
3440 [hitHTMLView _setMouseDownEvent:event];
3441 if ([hitHTMLView _isSelectionEvent:event]) {
3442 if (Page* page = coreFrame->page())
3443 result = coreFrame->eventHandler()->eventMayStartDrag(PlatformEventFactory::createPlatformMouseEvent(event, page->chrome()->platformPageClient()));
3444 } else if ([hitHTMLView _isScrollBarEvent:event])
3446 [hitHTMLView _setMouseDownEvent:nil];