2 * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #import <WebKit/WebHTMLView.h>
31 #import <ApplicationServices/ApplicationServices.h>
32 #import <WebKit/DOM.h>
33 #import <WebKit/DOMExtensions.h>
34 #import <WebKit/DOMPrivate.h>
35 #import <WebKit/WebArchive.h>
36 #import <WebKit/WebArchiver.h>
37 #import <WebKit/WebBaseNetscapePluginViewInternal.h>
38 #import <WebKit/WebFrameBridge.h>
39 #import <WebKit/WebClipView.h>
40 #import <WebCore/WebDataProtocol.h>
41 #import <WebKit/WebDataSourceInternal.h>
42 #import <WebKit/WebDefaultUIDelegate.h>
43 #import <WebKit/WebDocumentInternal.h>
44 #import <WebKit/WebDOMOperationsPrivate.h>
45 #import <WebKit/WebEditingDelegate.h>
46 #import <WebKit/WebElementDictionary.h>
47 #import <WebKit/WebFramePrivate.h>
48 #import <WebKit/WebFrameInternal.h>
49 #import <WebKit/WebFrameViewInternal.h>
50 #import <WebKit/WebHTMLViewInternal.h>
51 #import <WebKit/WebHTMLRepresentationPrivate.h>
52 #import <WebKit/WebKitLogging.h>
53 #import <WebKit/WebKitNSStringExtras.h>
54 #import <WebKit/WebLocalizableStrings.h>
55 #import <WebKit/WebNetscapePluginEmbeddedView.h>
56 #import <WebKit/WebNSAttributedStringExtras.h>
57 #import <WebKit/WebNSEventExtras.h>
58 #import <WebKit/WebNSFileManagerExtras.h>
59 #import <WebKit/WebNSImageExtras.h>
60 #import <WebKit/WebNSObjectExtras.h>
61 #import <WebKit/WebNSPasteboardExtras.h>
62 #import <WebKit/WebNSPrintOperationExtras.h>
63 #import <WebKit/WebNSURLExtras.h>
64 #import <WebKit/WebNSViewExtras.h>
65 #import <WebKit/WebPluginController.h>
66 #import <WebKit/WebPreferences.h>
67 #import <WebKit/WebPreferencesPrivate.h>
68 #import <WebKit/WebResourcePrivate.h>
69 #import <WebKit/WebStringTruncator.h>
70 #import <WebKit/WebUIDelegatePrivate.h>
71 #import <WebKit/WebViewInternal.h>
72 #import <WebKitSystemInterface.h>
73 #import <WebCore/WebCoreTextRenderer.h>
75 #import <AppKit/NSAccessibility.h>
77 // Included so usage of _NSSoftLinkingGetFrameworkFuncPtr will compile
78 #import <mach-o/dyld.h>
81 // need to declare this because AppKit does not make it available as API or SPI
82 extern NSString *NSMarkedClauseSegmentAttributeName;
83 extern NSString *NSTextInputReplacementRangeAttributeName;
85 // Kill ring calls. Would be better to use NSKillRing.h, but that's not available in SPI.
86 void _NSInitializeKillRing(void);
87 void _NSAppendToKillRing(NSString *);
88 void _NSPrependToKillRing(NSString *);
89 NSString *_NSYankFromKillRing(void);
90 NSString *_NSYankPreviousFromKillRing(void);
91 void _NSNewKillRingSequence(void);
92 void _NSSetKillRingToYankedState(void);
93 void _NSResetKillRingOperationFlag(void);
95 @interface NSView (AppKitSecretsIKnowAbout)
96 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
97 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
99 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
100 - (void)_propagateDirtyRectsToOpaqueAncestors;
103 @interface NSApplication (AppKitSecretsIKnowAbout)
104 - (void)speakString:(NSString *)string;
107 @interface NSWindow (AppKitSecretsIKnowAbout)
108 - (id)_newFirstResponderAfterResigning;
111 @interface NSAttributedString (AppKitSecretsIKnowAbout)
112 - (id)_initWithDOMRange:(DOMRange *)domRange;
113 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
116 @interface NSSpellChecker (CurrentlyPrivateForTextView)
117 - (void)learnWord:(NSString *)word;
120 // By imaging to a width a little wider than the available pixels,
121 // thin pages will be scaled down a little, matching the way they
122 // print in IE and Camino. This lets them use fewer sheets than they
123 // would otherwise, which is presumably why other browsers do this.
124 // Wide pages will be scaled down more than this.
125 #define PrintingMinimumShrinkFactor 1.25f
127 // This number determines how small we are willing to reduce the page content
128 // in order to accommodate the widest line. If the page would have to be
129 // reduced smaller to make the widest line fit, we just clip instead (this
130 // behavior matches MacIE and Mozilla, at least)
131 #define PrintingMaximumShrinkFactor 2.0f
133 // This number determines how short the last printed page of a multi-page print session
134 // can be before we try to shrink the scale in order to reduce the number of pages, and
135 // thus eliminate the orphan.
136 #define LastPrintedPageOrphanRatio 0.1f
138 // This number determines the amount the scale factor is adjusted to try to eliminate orphans.
139 // It has no direct mathematical relationship to LastPrintedPageOrphanRatio, due to variable
140 // numbers of pages, logic to avoid breaking elements, and CSS-supplied hard page breaks.
141 #define PrintingOrphanShrinkAdjustment 1.1f
143 #define AUTOSCROLL_INTERVAL 0.1f
145 #define DRAG_LABEL_BORDER_X 4.0f
146 #define DRAG_LABEL_BORDER_Y 2.0f
147 #define DRAG_LABEL_RADIUS 5.0f
148 #define DRAG_LABEL_BORDER_Y_OFFSET 2.0f
150 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP 120.0f
151 #define MAX_DRAG_LABEL_WIDTH 320.0f
153 #define DRAG_LINK_LABEL_FONT_SIZE 11.0f
154 #define DRAG_LINK_URL_FONT_SIZE 10.0f
156 // Any non-zero value will do, but using something recognizable might help us debug some day.
157 #define TRACKING_RECT_TAG 0xBADFACE
159 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
160 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
162 #define STANDARD_WEIGHT 5
163 #define MIN_BOLD_WEIGHT 9
164 #define STANDARD_BOLD_WEIGHT 10
167 deleteSelectionAction,
169 forwardDeleteKeyAction
172 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
173 static BOOL forceNSViewHitTest = NO;
175 // if YES, do the "top WebHTMLView" it test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721])
176 static BOOL forceWebHTMLViewHitTest = NO;
178 // Used to avoid linking with ApplicationServices framework for _DCMDictionaryServiceWindowShow
179 void *_NSSoftLinkingGetFrameworkFuncPtr(NSString *inUmbrellaFrameworkName,
180 NSString *inFrameworkName,
181 const char *inFuncName,
182 const struct mach_header **ioCachedFrameworkImageHeaderPtr);
185 @interface WebHTMLView (WebTextSizing) <_WebDocumentTextSizing>
188 @interface WebHTMLView (WebHTMLViewFileInternal)
189 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
190 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText chosePlainText:(BOOL *)chosePlainText;
191 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
192 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
193 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
194 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
195 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
196 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
197 - (float)_calculatePrintHeight;
198 - (void)_updateTextSizeMultiplier;
199 - (DOMRange *)_selectedRange;
200 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
201 - (void)_deleteRange:(DOMRange *)range
202 killRing:(BOOL)killRing
203 prepend:(BOOL)prepend
204 smartDeleteOK:(BOOL)smartDeleteOK
205 deletionAction:(WebDeletionAction)deletionAction
206 granularity:(WebBridgeSelectionGranularity)granularity;
207 - (void)_deleteSelection;
208 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard;
209 - (NSView *)_hitViewForEvent:(NSEvent *)event;
210 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
211 - (DOMRange *)_documentRange;
212 - (WebFrameBridge *)_bridge;
213 - (void)_setMouseDownEvent:(NSEvent *)event;
216 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
217 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize;
220 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
221 - (void)_updateSelectionForInputManager;
222 - (void)_insertText:(NSString *)text selectInsertedText:(BOOL)selectText;
225 @interface WebHTMLView (WebEditingStyleSupport)
226 - (DOMCSSStyleDeclaration *)_emptyStyle;
227 - (NSString *)_colorAsString:(NSColor *)color;
230 @interface NSView (WebHTMLViewFileInternal)
231 - (void)_web_setPrintingModeRecursive;
232 - (void)_web_clearPrintingModeRecursive;
233 - (void)_web_layoutIfNeededRecursive;
234 - (void)_web_layoutIfNeededRecursive:(NSRect)rect testDirtyRect:(bool)testDirtyRect;
237 @interface NSMutableDictionary (WebHTMLViewFileInternal)
238 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
241 // Handles the complete: text command
242 @interface WebTextCompleteController : NSObject
246 NSWindow *_popupWindow;
247 NSTableView *_tableView;
248 NSArray *_completions;
249 NSString *_originalString;
252 - (id)initWithHTMLView:(WebHTMLView *)view;
253 - (void)doCompletion;
254 - (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft;
255 - (BOOL)filterKeyDown:(NSEvent *)event;
256 - (void)_reflectSelection;
259 @implementation WebHTMLViewPrivate
263 ASSERT(autoscrollTimer == nil);
264 ASSERT(autoscrollTriggerEvent == nil);
266 [mouseDownEvent release];
267 [draggingImageURL release];
268 [pluginController release];
270 [compController release];
271 [firstResponderTextViewAtMouseDownTime release];
272 [dataSource release];
273 [highlighters release];
280 @implementation WebHTMLView (WebHTMLViewFileInternal)
282 - (DOMRange *)_documentRange
284 return [[[self _bridge] DOMDocument] _documentRange];
287 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
289 NSArray *imageMIMETypes = [WebFrameBridge supportedImageResourceMIMETypes];
290 NSEnumerator *enumerator = [paths objectEnumerator];
293 while ((path = [enumerator nextObject]) != nil) {
294 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
295 if ([imageMIMETypes containsObject:MIMEType]) {
303 - (WebDataSource *)_dataSource
305 return _private->dataSource;
308 - (WebFrameBridge *)_bridge
310 return [_private->dataSource _bridge];
313 - (WebView *)_webView
315 return [_private->dataSource _webView];
318 - (WebFrameView *)_frameView
320 return [[_private->dataSource webFrame] frameView];
323 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
325 DOMDocumentFragment *fragment;
326 NSArray *imageMIMETypes = [WebFrameBridge supportedImageResourceMIMETypes];
327 NSEnumerator *enumerator = [paths objectEnumerator];
328 WebDataSource *dataSource = [self _dataSource];
329 NSMutableArray *domNodes = [[NSMutableArray alloc] init];
332 while ((path = [enumerator nextObject]) != nil) {
333 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
334 if ([imageMIMETypes containsObject:MIMEType]) {
335 WebResource *resource = [[WebResource alloc] initWithData:[NSData dataWithContentsOfFile:path]
336 URL:[NSURL fileURLWithPath:path]
341 [domNodes addObject:[dataSource _imageElementWithImageResource:resource]];
345 // Non-image file types
346 NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
347 [domNodes addObject:[[[self _bridge] DOMDocument] createTextNode: url]];
351 fragment = [[self _bridge] documentFragmentWithNodesAsParagraphs:domNodes];
355 return [fragment firstChild] != nil ? fragment : nil;
358 + (NSArray *)_excludedElementsForAttributedStringConversion
360 static NSArray *elements = nil;
361 if (elements == nil) {
362 elements = [[NSArray alloc] initWithObjects:
363 // Omit style since we want style to be inline so the fragment can be easily inserted.
365 // Omit xml so the result is not XHTML.
367 // Omit tags that will get stripped when converted to a fragment anyway.
368 @"doctype", @"html", @"head", @"body",
369 // Omit deprecated tags.
370 @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
371 // Omit object so no file attachments are part of the fragment.
377 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
378 inContext:(DOMRange *)context
379 allowPlainText:(BOOL)allowPlainText
380 chosePlainText:(BOOL *)chosePlainText
382 NSArray *types = [pasteboard types];
383 *chosePlainText = NO;
385 if ([types containsObject:WebArchivePboardType]) {
386 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
388 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
396 if ([types containsObject:NSFilenamesPboardType]) {
397 DOMDocumentFragment *fragment = [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
398 if (fragment != nil) {
405 if ([types containsObject:NSHTMLPboardType]) {
406 NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
407 // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
408 if ([HTMLString hasPrefix:@"Version:"]) {
409 NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
410 if (range.location != NSNotFound) {
411 HTMLString = [HTMLString substringFromIndex:range.location];
414 if ([HTMLString length] != 0) {
415 return [[self _bridge] documentFragmentWithMarkupString:HTMLString baseURLString:nil];
419 NSAttributedString *string = nil;
420 if ([types containsObject:NSRTFDPboardType]) {
421 string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
423 if (string == nil && [types containsObject:NSRTFPboardType]) {
424 string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
427 NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
428 [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
429 self, @"WebResourceHandler", nil];
430 NSArray *subresources;
431 DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length])
432 document:[[self _bridge] DOMDocument]
433 documentAttributes:documentAttributes
434 subresources:&subresources];
435 [documentAttributes release];
440 if ([types containsObject:NSTIFFPboardType]) {
441 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
442 URL:[NSURL _web_uniqueWebDataURLWithRelativeString:@"/image.tiff"]
443 MIMEType:@"image/tiff"
446 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
451 if ([types containsObject:NSPICTPboardType]) {
452 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
453 URL:[NSURL _web_uniqueWebDataURLWithRelativeString:@"/image.pict"]
454 MIMEType:@"image/pict"
457 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
462 if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
463 DOMDocument* document = [[self _bridge] DOMDocument];
466 DOMHTMLAnchorElement* anchor = (DOMHTMLAnchorElement*)[document createElement:@"a"];
467 NSString *URLString = [URL _web_userVisibleString];
468 NSString *URLTitleString = [pasteboard stringForType:WebURLNamePboardType];
469 DOMText* text = [document createTextNode:URLTitleString];
470 [anchor setHref:URLString];
471 [anchor appendChild:text];
472 DOMDocumentFragment* fragment = [document createDocumentFragment];
473 [fragment appendChild:anchor];
474 if ([URLString length] > 0)
479 if (allowPlainText && [types containsObject:NSStringPboardType]) {
480 *chosePlainText = YES;
481 return [[self _bridge] documentFragmentWithText:[pasteboard stringForType:NSStringPboardType]
488 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
490 NSArray *types = [pasteboard types];
492 if ([types containsObject:NSStringPboardType])
493 return [pasteboard stringForType:NSStringPboardType];
495 NSAttributedString *attributedString = nil;
498 if ([types containsObject:NSRTFDPboardType])
499 attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
500 if (attributedString == nil && [types containsObject:NSRTFPboardType])
501 attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
502 if (attributedString != nil) {
503 string = [[attributedString string] copy];
504 [attributedString release];
505 return [string autorelease];
508 if ([types containsObject:NSFilenamesPboardType]) {
509 string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
516 if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
517 string = [URL _web_userVisibleString];
518 if ([string length] > 0)
525 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
527 DOMRange *range = [self _selectedRange];
529 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard
530 inContext:range allowPlainText:allowPlainText chosePlainText:&chosePlainText];
531 WebFrameBridge *bridge = [self _bridge];
532 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[self _selectedRange] givenAction:WebViewInsertActionPasted]) {
533 [bridge replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:chosePlainText];
537 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
539 NSString *text = [self _plainTextFromPasteboard:pasteboard];
540 if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
541 [[self _bridge] replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
544 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
546 WebView *webView = [self _webView];
547 DOMNode *child = [fragment firstChild];
548 if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
549 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
550 return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
553 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
555 WebView *webView = [self _webView];
556 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
559 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
561 return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
564 // Calculate the vertical size of the view that fits on a single page
565 - (float)_calculatePrintHeight
567 // Obtain the print info object for the current operation
568 NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
570 // Calculate the page height in points
571 NSSize paperSize = [pi paperSize];
572 return paperSize.height - [pi topMargin] - [pi bottomMargin];
575 - (void)_updateTextSizeMultiplier
577 [[self _bridge] setTextSizeMultiplier:[[self _webView] textSizeMultiplier]];
580 - (DOMRange *)_selectedRange
582 return [[self _bridge] selectedDOMRange];
585 - (BOOL)_shouldDeleteRange:(DOMRange *)range
587 if (range == nil || [range collapsed])
590 if (![[self _bridge] canDeleteRange:range])
593 WebView *webView = [self _webView];
594 return [[webView _editingDelegateForwarder] webView:webView shouldDeleteDOMRange:range];
597 - (void)_deleteRange:(DOMRange *)range
598 killRing:(BOOL)killRing
599 prepend:(BOOL)prepend
600 smartDeleteOK:(BOOL)smartDeleteOK
601 deletionAction:(WebDeletionAction)deletionAction
602 granularity:(WebBridgeSelectionGranularity)granularity
604 WebFrameBridge *bridge = [self _bridge];
605 BOOL smartDelete = smartDeleteOK ? [self _canSmartCopyOrDelete] : NO;
607 BOOL startNewKillRingSequence = _private->startNewKillRingSequence;
610 if (startNewKillRingSequence) {
611 _NSNewKillRingSequence();
613 NSString *string = [bridge stringForRange:range];
615 _NSPrependToKillRing(string);
617 _NSAppendToKillRing(string);
619 startNewKillRingSequence = NO;
622 switch (deletionAction) {
623 case deleteSelectionAction:
624 [bridge setSelectedDOMRange:range affinity:NSSelectionAffinityDownstream closeTyping:YES];
625 [bridge deleteSelectionWithSmartDelete:smartDelete];
627 case deleteKeyAction:
628 [bridge setSelectedDOMRange:range affinity:NSSelectionAffinityDownstream closeTyping:(granularity != WebBridgeSelectByCharacter)];
629 [bridge deleteKeyPressedWithSmartDelete:smartDelete granularity:granularity];
631 case forwardDeleteKeyAction:
632 [bridge setSelectedDOMRange:range affinity:NSSelectionAffinityDownstream closeTyping:(granularity != WebBridgeSelectByCharacter)];
633 [bridge forwardDeleteKeyPressedWithSmartDelete:smartDelete granularity:granularity];
637 _private->startNewKillRingSequence = startNewKillRingSequence;
640 - (void)_deleteSelection
642 [self _deleteRange:[self _selectedRange]
646 deletionAction:deleteSelectionAction
647 granularity:WebBridgeSelectByCharacter];
650 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
652 return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
655 - (NSView *)_hitViewForEvent:(NSEvent *)event
657 // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.
658 // Callers of this method, however, want to query the deepest view instead.
659 forceNSViewHitTest = YES;
660 NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
661 forceNSViewHitTest = NO;
665 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
667 // Put HTML on the pasteboard.
668 if ([types containsObject:WebArchivePboardType]) {
669 WebArchive *archive = [WebArchiver archiveSelectionInFrame:[self _frame]];
670 [pasteboard setData:[archive data] forType:WebArchivePboardType];
673 // Put the attributed string on the pasteboard (RTF/RTFD format).
674 if ([types containsObject:NSRTFDPboardType]) {
675 if (attributedString == nil) {
676 attributedString = [self selectedAttributedString];
678 NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
679 [pasteboard setData:RTFDData forType:NSRTFDPboardType];
681 if ([types containsObject:NSRTFPboardType]) {
682 if (attributedString == nil) {
683 attributedString = [self selectedAttributedString];
685 if ([attributedString containsAttachments]) {
686 attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
688 NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
689 [pasteboard setData:RTFData forType:NSRTFPboardType];
692 // Put plain string on the pasteboard.
693 if ([types containsObject:NSStringPboardType]) {
694 // Map to a plain old space because this is better for source code, other browsers do it,
695 // and because HTML forces you to do this any time you want two spaces in a row.
696 NSMutableString *s = [[self selectedString] mutableCopy];
697 const unichar NonBreakingSpaceCharacter = 0xA0;
698 NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
699 [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
700 [pasteboard setString:s forType:NSStringPboardType];
704 if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
705 [pasteboard setData:nil forType:WebSmartPastePboardType];
709 - (void)_setMouseDownEvent:(NSEvent *)event
711 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
713 if (event == _private->mouseDownEvent)
717 [_private->mouseDownEvent release];
718 _private->mouseDownEvent = event;
720 [_private->firstResponderTextViewAtMouseDownTime release];
722 // The only code that checks this ivar only cares about NSTextViews. The code used to be more general,
723 // but it caused reference cycles leading to world leaks (see 4557386). We should be able to eliminate
724 // firstResponderTextViewAtMouseDownTime entirely when all the form controls are native widgets, because
725 // the only caller (in WebCore) will be unnecessary.
727 NSResponder *firstResponder = [[self window] firstResponder];
728 if ([firstResponder isKindOfClass:[NSTextView class]])
729 _private->firstResponderTextViewAtMouseDownTime = [firstResponder retain];
731 _private->firstResponderTextViewAtMouseDownTime = nil;
733 _private->firstResponderTextViewAtMouseDownTime = nil;
738 @implementation WebHTMLView (WebPrivate)
740 + (NSArray *)supportedMIMETypes
742 return [WebHTMLRepresentation supportedMIMETypes];
745 + (NSArray *)supportedImageMIMETypes
747 return [WebHTMLRepresentation supportedImageMIMETypes];
750 + (NSArray *)supportedNonImageMIMETypes
752 return [WebHTMLRepresentation supportedNonImageMIMETypes];
755 + (NSArray *)unsupportedTextMIMETypes
757 return [NSArray arrayWithObjects:
758 @"text/calendar", // iCal
762 @"text/vcard", // vCard
765 @"text/ldif", // Netscape Address Book
766 @"text/qif", // Quicken
768 @"text/x-csv", // CSV (for Address Book and Microsoft Outlook)
769 @"text/x-vcf", // vCard type used in Sun affinity app
770 @"text/rtf", // Rich Text Format
774 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
776 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
777 location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
778 modifierFlags:[flagsChangedEvent modifierFlags]
779 timestamp:[flagsChangedEvent timestamp]
780 windowNumber:[flagsChangedEvent windowNumber]
781 context:[flagsChangedEvent context]
782 eventNumber:0 clickCount:0 pressure:0];
784 // Pretend it's a mouse move.
785 [[NSNotificationCenter defaultCenter]
786 postNotificationName:WKMouseMovedNotification() object:self
787 userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
790 - (void)_updateMouseoverWithFakeEvent
792 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
793 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
794 modifierFlags:[[NSApp currentEvent] modifierFlags]
795 timestamp:[NSDate timeIntervalSinceReferenceDate]
796 windowNumber:[[self window] windowNumber]
797 context:[[NSApp currentEvent] context]
798 eventNumber:0 clickCount:0 pressure:0];
800 [self _updateMouseoverWithEvent:fakeEvent];
803 - (void)_frameOrBoundsChanged
805 if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
806 [self setNeedsLayout:YES];
807 [self setNeedsDisplay:YES];
808 [_private->compController endRevertingChange:NO moveLeft:NO];
811 NSPoint origin = [[self superview] bounds].origin;
812 if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
813 [[self _bridge] sendScrollEvent];
814 [_private->compController endRevertingChange:NO moveLeft:NO];
816 WebView *webView = [self _webView];
817 [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
819 _private->lastScrollPosition = origin;
821 SEL selector = @selector(_updateMouseoverWithFakeEvent);
822 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:selector object:nil];
823 [self performSelector:selector withObject:nil afterDelay:0];
826 - (void)_setAsideSubviews
828 ASSERT(!_private->subviewsSetAside);
829 ASSERT(_private->savedSubviews == nil);
830 _private->savedSubviews = _subviews;
832 _private->subviewsSetAside = YES;
835 - (void)_restoreSubviews
837 ASSERT(_private->subviewsSetAside);
838 ASSERT(_subviews == nil);
839 _subviews = _private->savedSubviews;
840 _private->savedSubviews = nil;
841 _private->subviewsSetAside = NO;
844 // This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
845 // That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty
846 // before doing it. As a compromise, when we're opaque we do the layout only when actually asked to
847 // draw, but when we're transparent we do the layout at this stage so views behind us know that they
848 // need to be redrawn (in case the layout causes some things to get dirtied).
849 - (void)_propagateDirtyRectsToOpaqueAncestors
851 if (![[self _webView] drawsBackground]) {
852 [self _web_layoutIfNeededRecursive];
854 [super _propagateDirtyRectsToOpaqueAncestors];
857 // Don't let AppKit even draw subviews. We take care of that.
858 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
860 // This helps when we print as part of a larger print process.
861 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
862 BOOL wasInPrintingMode = _private->printing;
863 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
864 if (wasInPrintingMode != isPrinting) {
866 [self _web_setPrintingModeRecursive];
868 [self _web_clearPrintingModeRecursive];
872 [self _web_layoutIfNeededRecursive: rect testDirtyRect:YES];
873 [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
875 [self _setAsideSubviews];
876 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect
877 rectIsVisibleRectForView:visibleView topView:topView];
878 [self _restoreSubviews];
880 if (wasInPrintingMode != isPrinting) {
881 if (wasInPrintingMode) {
882 [self _web_setPrintingModeRecursive];
884 [self _web_clearPrintingModeRecursive];
889 // Don't let AppKit even draw subviews. We take care of that.
890 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
892 BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
894 BOOL wasInPrintingMode = _private->printing;
895 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
897 if (needToSetAsideSubviews) {
898 // This helps when we print as part of a larger print process.
899 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
900 if (wasInPrintingMode != isPrinting) {
902 [self _web_setPrintingModeRecursive];
904 [self _web_clearPrintingModeRecursive];
908 NSRect boundsBeforeLayout = [self bounds];
909 [self _web_layoutIfNeededRecursive: visRect testDirtyRect:NO];
911 // If layout changes the view's bounds, then we need to recompute the visRect.
912 // That's because the visRect passed to us was based on the bounds at the time
913 // we were called. This method is only displayed to draw "all", so it's safe
914 // to just call visibleRect to compute the entire rectangle.
915 if (!NSEqualRects(boundsBeforeLayout, [self bounds])) {
916 visRect = [self visibleRect];
919 [self _setAsideSubviews];
922 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
924 if (needToSetAsideSubviews) {
925 if (wasInPrintingMode != isPrinting) {
926 if (wasInPrintingMode) {
927 [self _web_setPrintingModeRecursive];
929 [self _web_clearPrintingModeRecursive];
933 [self _restoreSubviews];
937 - (WebHTMLView *)_topHTMLView
939 WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
940 ASSERT([view isKindOfClass:[WebHTMLView class]]);
944 - (BOOL)_isTopHTMLView
946 return self == [self _topHTMLView];
949 - (BOOL)_insideAnotherHTMLView
951 return self != [self _topHTMLView];
954 - (void)scrollPoint:(NSPoint)point
956 // Since we can't subclass NSTextView to do what we want, we have to second guess it here.
957 // If we get called during the handling of a key down event, we assume the call came from
958 // NSTextView, and ignore it and use our own code to decide how to page up and page down
959 // We are smarter about how far to scroll, and we have "superview scrolling" logic.
960 NSEvent *event = [[self window] currentEvent];
961 if ([event type] == NSKeyDown) {
962 const unichar pageUp = NSPageUpFunctionKey;
963 if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageUp length:1]].length == 1) {
964 [self tryToPerform:@selector(scrollPageUp:) with:nil];
967 const unichar pageDown = NSPageDownFunctionKey;
968 if ([[event characters] rangeOfString:[NSString stringWithCharacters:&pageDown length:1]].length == 1) {
969 [self tryToPerform:@selector(scrollPageDown:) with:nil];
974 [super scrollPoint:point];
977 - (NSView *)hitTest:(NSPoint)point
979 // WebHTMLView objects handle all events for objects inside them.
980 // To get those events, we prevent hit testing from AppKit.
982 // But there are three exceptions to this:
983 // 1) For right mouse clicks and control clicks we don't yet have an implementation
984 // that works for nested views, so we let the hit testing go through the
985 // standard NSView code path (needs to be fixed, see bug 4361618).
986 // 2) Java depends on doing a hit test inside it's mouse moved handling,
987 // so we let the hit testing go through the standard NSView code path
988 // when the current event is a mouse move (except when we are calling
989 // from _updateMouseoverWithEvent, so we have to use a global,
990 // forceWebHTMLViewHitTest, for that)
991 // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
992 // both need to figure out which view to check with inside the WebHTMLView.
993 // They use a global to change the behavior of hitTest: so they can get the
994 // right view. The global is forceNSViewHitTest and the method they use to
995 // do the hit testing is _hitViewForEvent:. (But this does not work correctly
996 // when there is HTML overlapping the view, see bug 4361626)
997 // 4) NSAccessibilityHitTest relies on this for checking the cursor position.
998 // Our check for that is whether the event is NSFlagsChanged. This works
999 // for VoiceOver's cntl-opt-f5 command (move focus to item under cursor)
1000 // and Dictionary's cmd-cntl-D (open dictionary popup for item under cursor).
1001 // This is of course a hack.
1003 BOOL captureHitsOnSubviews;
1004 if (forceNSViewHitTest)
1005 captureHitsOnSubviews = NO;
1006 else if (forceWebHTMLViewHitTest)
1007 captureHitsOnSubviews = YES;
1009 NSEvent *event = [[self window] currentEvent];
1010 captureHitsOnSubviews = !([event type] == NSMouseMoved
1011 || [event type] == NSRightMouseDown
1012 || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1013 || [event type] == NSFlagsChanged);
1016 if (!captureHitsOnSubviews)
1017 return [super hitTest:point];
1018 if ([[self superview] mouse:point inRect:[self frame]])
1023 static WebHTMLView *lastHitView = nil;
1025 - (void)_clearLastHitViewIfSelf
1027 if (lastHitView == self) {
1032 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1034 ASSERT(_private->trackingRectOwner == nil);
1035 _private->trackingRectOwner = owner;
1036 _private->trackingRectUserData = data;
1037 return TRACKING_RECT_TAG;
1040 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1042 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1043 ASSERT(_private->trackingRectOwner == nil);
1044 _private->trackingRectOwner = owner;
1045 _private->trackingRectUserData = data;
1046 return TRACKING_RECT_TAG;
1049 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1052 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1053 ASSERT(_private->trackingRectOwner == nil);
1054 _private->trackingRectOwner = owner;
1055 _private->trackingRectUserData = userDataList[0];
1056 trackingNums[0] = TRACKING_RECT_TAG;
1059 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1064 if (_private && (tag == TRACKING_RECT_TAG)) {
1065 _private->trackingRectOwner = nil;
1069 if (_private && (tag == _private->lastToolTipTag)) {
1070 [super removeTrackingRect:tag];
1071 _private->lastToolTipTag = 0;
1075 // If any other tracking rect is being removed, we don't know how it was created
1076 // and it's possible there's a leak involved (see 3500217)
1077 ASSERT_NOT_REACHED();
1080 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1083 for (i = 0; i < count; ++i) {
1087 ASSERT(tag == TRACKING_RECT_TAG);
1088 if (_private != nil) {
1089 _private->trackingRectOwner = nil;
1094 - (void)_sendToolTipMouseExited
1096 // Nothing matters except window, trackingNumber, and userData.
1097 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1098 location:NSMakePoint(0, 0)
1101 windowNumber:[[self window] windowNumber]
1104 trackingNumber:TRACKING_RECT_TAG
1105 userData:_private->trackingRectUserData];
1106 [_private->trackingRectOwner mouseExited:fakeEvent];
1109 - (void)_sendToolTipMouseEntered
1111 // Nothing matters except window, trackingNumber, and userData.
1112 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1113 location:NSMakePoint(0, 0)
1116 windowNumber:[[self window] windowNumber]
1119 trackingNumber:TRACKING_RECT_TAG
1120 userData:_private->trackingRectUserData];
1121 [_private->trackingRectOwner mouseEntered:fakeEvent];
1124 - (void)_setToolTip:(NSString *)string
1126 NSString *toolTip = [string length] == 0 ? nil : string;
1127 NSString *oldToolTip = _private->toolTip;
1128 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1132 [self _sendToolTipMouseExited];
1133 [oldToolTip release];
1135 _private->toolTip = [toolTip copy];
1137 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1138 [self removeAllToolTips];
1139 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1140 _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1141 [self _sendToolTipMouseEntered];
1145 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1147 return [[_private->toolTip copy] autorelease];
1150 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1152 if (_private->closed)
1155 NSView *contentView = [[event window] contentView];
1156 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1158 forceWebHTMLViewHitTest = YES;
1159 NSView *hitView = [contentView hitTest:locationForHitTest];
1160 forceWebHTMLViewHitTest = NO;
1162 WebHTMLView *view = nil;
1163 if ([hitView isKindOfClass:[WebHTMLView class]])
1164 view = (WebHTMLView *)hitView;
1169 if (lastHitView != view && lastHitView != nil) {
1170 // If we are moving out of a view (or frame), let's pretend the mouse moved
1171 // all the way out of that view. But we have to account for scrolling, because
1172 // khtml doesn't understand our clipping.
1173 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1174 float yScroll = visibleRect.origin.y;
1175 float xScroll = visibleRect.origin.x;
1177 event = [NSEvent mouseEventWithType:NSMouseMoved
1178 location:NSMakePoint(-1 - xScroll, -1 - yScroll )
1179 modifierFlags:[[NSApp currentEvent] modifierFlags]
1180 timestamp:[NSDate timeIntervalSinceReferenceDate]
1181 windowNumber:[[view window] windowNumber]
1182 context:[[NSApp currentEvent] context]
1183 eventNumber:0 clickCount:0 pressure:0];
1184 [[lastHitView _bridge] mouseMoved:event];
1190 [[view _bridge] mouseMoved:event];
1192 NSPoint point = [view convertPoint:[event locationInWindow] fromView:nil];
1193 NSDictionary *element = [view elementAtPoint:point];
1195 // Have the web view send a message to the delegate so it can do status bar display.
1196 [[view _webView] _mouseDidMoveOverElement:element modifierFlags:[event modifierFlags]];
1198 // Set a tool tip; it won't show up right away but will if the user pauses.
1199 NSString *newToolTip = nil;
1200 if (_private->showsURLsInToolTips) {
1201 DOMHTMLElement *domElement = [element objectForKey:WebElementDOMNodeKey];
1202 if ([domElement isKindOfClass:[DOMHTMLInputElement class]]) {
1203 if ([[(DOMHTMLInputElement *)domElement type] isEqualToString:@"submit"])
1204 newToolTip = [[(DOMHTMLInputElement *) domElement form] action];
1206 if (newToolTip == nil)
1207 newToolTip = [[element objectForKey:WebElementLinkURLKey] _web_userVisibleString];
1209 if (newToolTip == nil)
1210 newToolTip = [element objectForKey:WebElementTitleKey];
1211 [view _setToolTip:newToolTip];
1217 + (NSArray *)_insertablePasteboardTypes
1219 static NSArray *types = nil;
1221 types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType,
1222 NSFilenamesPboardType, NSTIFFPboardType, NSPICTPboardType, NSURLPboardType,
1223 NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, nil];
1228 + (NSArray *)_selectionPasteboardTypes
1230 // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1231 return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1234 - (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
1236 NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
1238 BOOL drawURLString = YES;
1239 BOOL clipURLString = NO, clipLabelString = NO;
1241 NSString *label = [element objectForKey: WebElementLinkLabelKey];
1242 NSString *urlString = [linkURL _web_userVisibleString];
1249 NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
1250 toHaveTrait:NSBoldFontMask];
1251 NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
1253 labelSize.width = [label _web_widthWithFont: labelFont];
1254 labelSize.height = [labelFont ascender] - [labelFont descender];
1255 if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
1256 labelSize.width = MAX_DRAG_LABEL_WIDTH;
1257 clipLabelString = YES;
1260 NSSize imageSize, urlStringSize;
1261 imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2.0f;
1262 imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2.0f;
1263 if (drawURLString) {
1264 urlStringSize.width = [urlString _web_widthWithFont: urlFont];
1265 urlStringSize.height = [urlFont ascender] - [urlFont descender];
1266 imageSize.height += urlStringSize.height;
1267 if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
1268 imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2.0f, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
1269 clipURLString = YES;
1271 imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2.0f, urlStringSize.width + DRAG_LABEL_BORDER_X * 2.0f);
1274 NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
1275 [dragImage lockFocus];
1277 [[NSColor colorWithCalibratedRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
1279 // Drag a rectangle with rounded corners/
1280 NSBezierPath *path = [NSBezierPath bezierPath];
1281 [path appendBezierPathWithOvalInRect: NSMakeRect(0.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1282 [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1283 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1284 [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1286 [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0.0f, imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height)];
1287 [path appendBezierPathWithRect: NSMakeRect(0.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1288 [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS - 20.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 20.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1291 NSColor *topColor = [NSColor colorWithCalibratedWhite:0.0f alpha:0.75f];
1292 NSColor *bottomColor = [NSColor colorWithCalibratedWhite:1.0f alpha:0.5f];
1293 if (drawURLString) {
1295 urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:urlFont];
1297 [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender])
1298 withTopColor:topColor bottomColor:bottomColor font:urlFont];
1301 if (clipLabelString)
1302 label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:labelFont];
1303 [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
1304 withTopColor:topColor bottomColor:bottomColor font:labelFont];
1306 [dragImage unlockFocus];
1311 - (BOOL)_startDraggingImage:(NSImage *)wcDragImage at:(NSPoint)wcDragLoc operation:(NSDragOperation)op event:(NSEvent *)mouseDraggedEvent sourceIsDHTML:(BOOL)srcIsDHTML DHTMLWroteData:(BOOL)dhtmlWroteData
1313 WebHTMLView *topHTMLView = [self _topHTMLView];
1314 if (self != topHTMLView) {
1315 [topHTMLView _setMouseDownEvent:_private->mouseDownEvent];
1316 BOOL result = [topHTMLView _startDraggingImage:wcDragImage at:wcDragLoc operation:op event:mouseDraggedEvent sourceIsDHTML:srcIsDHTML DHTMLWroteData:dhtmlWroteData];
1317 [topHTMLView _setMouseDownEvent:nil];
1321 NSPoint mouseDownPoint = [self convertPoint:[_private->mouseDownEvent locationInWindow] fromView:nil];
1322 NSDictionary *element = [self elementAtPoint:mouseDownPoint allowShadowContent:YES];
1324 NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
1325 NSURL *imageURL = [element objectForKey:WebElementImageURLKey];
1326 BOOL isSelected = [[element objectForKey:WebElementIsSelectedKey] boolValue];
1328 [_private->draggingImageURL release];
1329 _private->draggingImageURL = nil;
1331 NSPoint mouseDraggedPoint = [self convertPoint:[mouseDraggedEvent locationInWindow] fromView:nil];
1332 _private->webCoreDragOp = op; // will be DragNone if WebCore doesn't care
1333 NSImage *dragImage = nil;
1334 NSPoint dragLoc = { 0, 0 }; // quiet gcc 4.0 warning
1336 // We allow WebCore to override the drag image, even if its a link, image or text we're dragging.
1337 // This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp.
1338 // We could verify that ActionDHTML is allowed, although WebCore does claim to respect the action.
1340 dragImage = wcDragImage;
1341 // wcDragLoc is the cursor position relative to the lower-left corner of the image.
1342 // We add in the Y dimension because we are a flipped view, so adding moves the image down.
1345 dragLoc = NSMakePoint(mouseDraggedPoint.x - wcDragLoc.x, mouseDraggedPoint.y + wcDragLoc.y);
1347 dragLoc = NSMakePoint(mouseDownPoint.x - wcDragLoc.x, mouseDownPoint.y + wcDragLoc.y);
1348 _private->dragOffset = wcDragLoc;
1351 WebView *webView = [self _webView];
1352 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1353 BOOL startedDrag = YES; // optimism - we almost always manage to start the drag
1355 // note per kwebster, the offset arg below is always ignored in positioning the image
1356 DOMNode *node = [element objectForKey:WebElementDOMNodeKey];
1357 WebHTMLView *innerHTMLView = (WebHTMLView *)[[[[node ownerDocument] webFrame] frameView] documentView];
1358 ASSERT([innerHTMLView isKindOfClass:[WebHTMLView class]]);
1360 && [node isKindOfClass:[DOMElement class]]
1361 && [(DOMElement *)node image]
1362 && (_private->dragSourceActionMask & WebDragSourceActionImage)) {
1364 if (!dhtmlWroteData) {
1365 // Select the image when it is dragged. This allows the image to be moved via MoveSelectionCommandImpl and this matches NSTextView's behavior.
1366 ASSERT(node != nil);
1367 [webView setSelectedDOMRange:[[node ownerDocument] _createRangeWithNode:node] affinity:NSSelectionAffinityDownstream];
1368 _private->draggingImageURL = [imageURL retain];
1370 WebArchive *archive;
1372 // If the image element comes from an ImageDocument, we don't want to
1373 // create a web archive from the image element.
1374 if ([[self _bridge] canSaveAsWebArchive])
1375 archive = [node webArchive];
1377 archive = [WebArchiver archiveMainResourceForFrame:[self _frame]];
1379 source = [pasteboard _web_declareAndWriteDragImageForElement:(DOMElement *)node
1380 URL:linkURL ? linkURL : imageURL
1381 title:[element objectForKey:WebElementImageAltStringKey]
1385 [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionImage fromPoint:mouseDownPoint withPasteboard:pasteboard];
1386 if (dragImage == nil)
1387 [self _web_DragImageForElement:(DOMElement *)node
1388 rect:[self convertRect:[[element objectForKey:WebElementImageRectKey] rectValue] fromView:innerHTMLView]
1389 event:_private->mouseDownEvent
1390 pasteboard:pasteboard
1392 offset:&_private->dragOffset];
1394 [self dragImage:dragImage
1397 event:_private->mouseDownEvent
1398 pasteboard:pasteboard
1401 } else if (linkURL && (_private->dragSourceActionMask & WebDragSourceActionLink)) {
1402 if (!dhtmlWroteData) {
1403 NSArray *types = [NSPasteboard _web_writableTypesForURL];
1404 [pasteboard declareTypes:types owner:self];
1405 [pasteboard _web_writeURL:linkURL andTitle:[element objectForKey:WebElementLinkLabelKey] types:types];
1407 [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionLink fromPoint:mouseDownPoint withPasteboard:pasteboard];
1408 if (dragImage == nil) {
1409 dragImage = [self _dragImageForLinkElement:element];
1410 NSSize offset = NSMakeSize([dragImage size].width / 2, -DRAG_LABEL_BORDER_Y);
1411 dragLoc = NSMakePoint(mouseDraggedPoint.x - offset.width, mouseDraggedPoint.y - offset.height);
1412 _private->dragOffset.x = offset.width;
1413 _private->dragOffset.y = -offset.height; // inverted because we are flipped
1415 // HACK: We should pass the mouseDown event instead of the mouseDragged! This hack gets rid of
1416 // a flash of the image at the mouseDown location when the drag starts.
1417 [self dragImage:dragImage
1420 event:mouseDraggedEvent
1421 pasteboard:pasteboard
1424 } else if (isSelected && (_private->dragSourceActionMask & WebDragSourceActionSelection)) {
1425 if (!dhtmlWroteData)
1426 [innerHTMLView _writeSelectionToPasteboard:pasteboard];
1427 [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionSelection fromPoint:mouseDownPoint withPasteboard:pasteboard];
1428 if (dragImage == nil) {
1429 dragImage = [innerHTMLView _selectionDraggingImage];
1430 NSRect draggingRect = [self convertRect:[innerHTMLView _selectionDraggingRect] fromView:innerHTMLView];
1431 dragLoc = NSMakePoint(NSMinX(draggingRect), NSMaxY(draggingRect));
1432 _private->dragOffset.x = mouseDownPoint.x - dragLoc.x;
1433 _private->dragOffset.y = dragLoc.y - mouseDownPoint.y; // inverted because we are flipped
1435 [self dragImage:dragImage
1438 event:_private->mouseDownEvent
1439 pasteboard:pasteboard
1442 } else if (srcIsDHTML) {
1443 ASSERT(_private->dragSourceActionMask & WebDragSourceActionDHTML);
1444 [[webView _UIDelegateForwarder] webView:webView willPerformDragSourceAction:WebDragSourceActionDHTML fromPoint:mouseDownPoint withPasteboard:pasteboard];
1445 if (dragImage == nil) {
1446 // WebCore should have given us an image, but we'll make one up
1447 // FIXME: Oops! I removed this image from WebKit. Is this a dead code path?
1448 NSString *imagePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"missing_image" ofType:@"tiff"];
1449 dragImage = [[[NSImage alloc] initWithContentsOfFile:imagePath] autorelease];
1450 NSSize imageSize = [dragImage size];
1451 dragLoc = NSMakePoint(mouseDownPoint.x - imageSize.width / 2, mouseDownPoint.y + imageSize.height / 2);
1452 _private->dragOffset.x = imageSize.width / 2;
1453 _private->dragOffset.y = imageSize.height / 2;
1455 [self dragImage:dragImage
1458 event:_private->mouseDownEvent
1459 pasteboard:pasteboard
1463 // Only way I know if to get here is if the original element clicked on in the mousedown is no longer
1464 // under the mousedown point, so linkURL, imageURL and isSelected are all false/nil.
1470 - (BOOL)_mayStartDragAtEventLocation:(NSPoint)location
1472 WebHTMLView *topHTMLView = [self _topHTMLView];
1473 if (self != topHTMLView)
1474 return [topHTMLView _mayStartDragAtEventLocation:location];
1476 NSPoint mouseDownPoint = [self convertPoint:location fromView:nil];
1477 NSDictionary *mouseDownElement = [self elementAtPoint:mouseDownPoint allowShadowContent:YES];
1479 ASSERT([self _webView]);
1481 if ([mouseDownElement objectForKey:WebElementImageKey]
1482 && [mouseDownElement objectForKey:WebElementImageURLKey]
1483 && [[[self _webView] preferences] loadsImagesAutomatically]
1484 && (_private->dragSourceActionMask & WebDragSourceActionImage))
1487 if ([mouseDownElement objectForKey:WebElementLinkURLKey]
1488 && (_private->dragSourceActionMask & WebDragSourceActionLink))
1491 if ([[mouseDownElement objectForKey:WebElementIsSelectedKey] boolValue]
1492 && (_private->dragSourceActionMask & WebDragSourceActionSelection))
1498 - (WebPluginController *)_pluginController
1500 return _private->pluginController;
1503 - (void)_web_setPrintingModeRecursive
1505 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
1506 [super _web_setPrintingModeRecursive];
1509 - (void)_web_clearPrintingModeRecursive
1511 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
1512 [super _web_clearPrintingModeRecursive];
1515 - (void)_layoutIfNeeded
1517 ASSERT(!_private->subviewsSetAside);
1519 if ([[self _bridge] needsLayout])
1520 _private->needsLayout = YES;
1521 if (_private->needsToApplyStyles || _private->needsLayout)
1525 - (void)_web_layoutIfNeededRecursive
1527 [self _layoutIfNeeded];
1528 [super _web_layoutIfNeededRecursive];
1531 - (void)_web_layoutIfNeededRecursive:(NSRect)displayRect testDirtyRect:(bool)testDirtyRect
1533 ASSERT(!_private->subviewsSetAside);
1535 displayRect = NSIntersectionRect(displayRect, [self bounds]);
1536 if (testDirtyRect) {
1537 NSRect dirtyRect = [self _dirtyRect];
1538 displayRect = NSIntersectionRect(displayRect, dirtyRect);
1540 if (!NSIsEmptyRect(displayRect))
1541 [self _layoutIfNeeded];
1543 [super _web_layoutIfNeededRecursive:displayRect testDirtyRect:NO];
1546 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1548 if (_private->autoscrollTimer == nil) {
1549 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1550 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1551 _private->autoscrollTriggerEvent = [triggerEvent retain];
1555 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1556 // We can't remove this yet because it's still in use by Mail.
1557 - (NSRect)_selectionRect
1559 return [self selectionRect];
1562 - (void)_stopAutoscrollTimer
1564 NSTimer *timer = _private->autoscrollTimer;
1565 _private->autoscrollTimer = nil;
1566 [_private->autoscrollTriggerEvent release];
1567 _private->autoscrollTriggerEvent = nil;
1574 // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1575 // a mouse up event.
1576 BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);
1578 [self _stopAutoscrollTimer];
1582 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1583 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1584 modifierFlags:[[NSApp currentEvent] modifierFlags]
1585 timestamp:[NSDate timeIntervalSinceReferenceDate]
1586 windowNumber:[[self window] windowNumber]
1587 context:[[NSApp currentEvent] context]
1588 eventNumber:0 clickCount:0 pressure:0];
1589 [self mouseDragged:fakeEvent];
1594 // Copying can be done regardless of whether you can edit.
1595 return [self _hasSelection] && [[self _bridge] mayCopy];
1600 return [self _canCopy] && [self _isEditable];
1605 return [self _hasSelection] && [self _isEditable];
1610 return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1615 return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1618 - (BOOL)_canEditRichly
1620 return [self _canEdit] && [[self _bridge] isSelectionRichlyEditable];
1623 - (BOOL)_canAlterCurrentSelection
1625 return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1628 - (BOOL)_hasSelection
1630 return [[self _bridge] selectionState] == WebSelectionStateRange;
1633 - (BOOL)_hasSelectionOrInsertionPoint
1635 return [[self _bridge] selectionState] != WebSelectionStateNone;
1638 - (BOOL)_hasInsertionPoint
1640 return [[self _bridge] selectionState] == WebSelectionStateCaret;
1645 return [[self _webView] isEditable] || [[self _bridge] isSelectionEditable];
1648 - (BOOL)_isSelectionInPasswordField
1650 return [[self _bridge] isSelectionInPasswordField];
1653 - (BOOL)_isSelectionMisspelled
1655 NSString *selectedString = [self selectedString];
1656 unsigned length = [selectedString length];
1660 NSRange range = [[NSSpellChecker sharedSpellChecker] checkSpellingOfString:selectedString
1664 inSpellDocumentWithTag:[[self _webView] spellCheckerDocumentTag]
1666 return range.length == length;
1669 - (NSArray *)_guessesForMisspelledSelection
1671 ASSERT([[self selectedString] length] != 0);
1672 return [[NSSpellChecker sharedSpellChecker] guessesForWord:[self selectedString]];
1675 - (void)_changeSpellingFromMenu:(id)sender
1677 ASSERT([[self selectedString] length] != 0);
1678 if ([self _shouldReplaceSelectionWithText:[sender title] givenAction:WebViewInsertActionPasted]) {
1679 [[self _bridge] replaceSelectionWithText:[sender title] selectReplacement:YES smartReplace:NO];
1683 - (void)_ignoreSpellingFromMenu:(id)sender
1685 ASSERT([[self selectedString] length] != 0);
1686 [[NSSpellChecker sharedSpellChecker] ignoreWord:[self selectedString] inSpellDocumentWithTag:[[self _webView] spellCheckerDocumentTag]];
1689 - (void)_learnSpellingFromMenu:(id)sender
1691 ASSERT([[self selectedString] length] != 0);
1692 [[NSSpellChecker sharedSpellChecker] learnWord:[self selectedString]];
1695 - (void)_lookUpInDictionaryFromMenu:(id)sender
1697 // This should only be called when there's a selection, but play it safe.
1698 if (![self _hasSelection]) {
1702 // Soft link to dictionary-display function to avoid linking another framework (ApplicationServices/LangAnalysis)
1703 static OSStatus (*__dictionaryServiceWindowShow)(id inWordString, NSRect inWordBoundary, UInt16 inLineDirection) = NULL;
1704 static const struct mach_header *frameworkImageHeader = NULL;
1705 static BOOL lookedForFunction = NO;
1706 if (!lookedForFunction) {
1707 __dictionaryServiceWindowShow = _NSSoftLinkingGetFrameworkFuncPtr(@"ApplicationServices", @"LangAnalysis", "_DCMDictionaryServiceWindowShow", &frameworkImageHeader);
1708 lookedForFunction = YES;
1710 if (!__dictionaryServiceWindowShow) {
1711 LOG_ERROR("Couldn't find _DCMDictionaryServiceWindowShow");
1715 // FIXME: must check for right-to-left here
1716 NSWritingDirection writingDirection = NSWritingDirectionLeftToRight;
1718 NSAttributedString *attrString = [self selectedAttributedString];
1719 // FIXME: the dictionary API expects the rect for the first line of selection. Passing
1720 // the rect for the entire selection, as we do here, positions the pop-up window near
1721 // the bottom of the selection rather than at the selected word.
1722 NSRect rect = [self convertRect:[[self _bridge] visibleSelectionRect] toView:nil];
1723 rect.origin = [[self window] convertBaseToScreen:rect.origin];
1724 NSData *data = [attrString RTFFromRange:NSMakeRange(0, [attrString length]) documentAttributes:nil];
1725 (void)__dictionaryServiceWindowShow(data, rect, (writingDirection == NSWritingDirectionRightToLeft) ? 1 : 0);
1728 - (BOOL)_transparentBackground
1730 return _private->transparentBackground;
1733 - (void)_setTransparentBackground:(BOOL)f
1735 _private->transparentBackground = f;
1738 - (NSImage *)_selectionDraggingImage
1740 if ([self _hasSelection]) {
1741 NSImage *dragImage = [[self _bridge] selectionImageForcingWhiteText:NO];
1742 [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1748 - (NSRect)_selectionDraggingRect
1750 // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1751 return [self selectionImageRect];
1754 - (BOOL)_canIncreaseSelectionListLevel
1756 return ([self _canEditRichly] && [[self _bridge] canIncreaseSelectionListLevel]);
1759 - (BOOL)_canDecreaseSelectionListLevel
1761 return ([self _canEditRichly] && [[self _bridge] canDecreaseSelectionListLevel]);
1764 - (DOMNode *)_increaseSelectionListLevel
1766 if (![self _canEditRichly])
1769 WebFrameBridge *bridge = [self _bridge];
1770 return [bridge increaseSelectionListLevel];
1773 - (DOMNode *)_increaseSelectionListLevelOrdered
1775 if (![self _canEditRichly])
1778 WebFrameBridge *bridge = [self _bridge];
1779 return [bridge increaseSelectionListLevelOrdered];
1782 - (DOMNode *)_increaseSelectionListLevelUnordered
1784 if (![self _canEditRichly])
1787 WebFrameBridge *bridge = [self _bridge];
1788 return [bridge increaseSelectionListLevelUnordered];
1791 - (void)_decreaseSelectionListLevel
1793 if (![self _canEditRichly])
1796 WebFrameBridge *bridge = [self _bridge];
1797 [bridge decreaseSelectionListLevel];
1800 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1802 if (!_private->highlighters)
1803 _private->highlighters = [[NSMutableDictionary alloc] init];
1804 [_private->highlighters setObject:highlighter forKey:type];
1807 - (void)_removeHighlighterOfType:(NSString*)type
1809 [_private->highlighters removeObjectForKey:type];
1812 - (BOOL)_web_firstResponderCausesFocusDisplay
1814 return [self _web_firstResponderIsSelfOrDescendantView] || [[self window] firstResponder] == [self _frameView];
1817 - (void)_updateActiveState
1819 // This method does the job of updating the view based on the view's firstResponder-ness and
1820 // the window key-ness of the window containing this view. This involves four kinds of
1821 // drawing updates right now, all handled in WebCore in response to the call over the bridge.
1823 // The four display attributes are as follows:
1825 // 1. The background color used to draw behind selected content (active | inactive color)
1826 // 2. Caret blinking (blinks | does not blink)
1827 // 3. The drawing of a focus ring around links in web pages.
1828 // 4. Changing the tint of controls from clear to aqua/graphite and vice versa
1830 // Also, this is responsible for letting the bridge know if the window has gained or lost focus
1831 // so we can send focus and blur events.
1833 NSWindow *window = [self window];
1834 BOOL windowIsKey = [window isKeyWindow];
1835 BOOL windowOrSheetIsKey = windowIsKey || [[window attachedSheet] isKeyWindow];
1837 BOOL isActive = !_private->resigningFirstResponder && windowIsKey && (_private->descendantBecomingFirstResponder || [self _web_firstResponderCausesFocusDisplay]);
1839 [[self _bridge] setWindowHasFocus:windowOrSheetIsKey];
1840 [[self _bridge] setIsActive:isActive];
1843 - (unsigned)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(unsigned)limit
1845 return [[self _bridge] markAllMatchesForText:string caseSensitive:caseFlag limit:limit];
1848 - (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
1850 [[self _bridge] setMarkedTextMatchesAreHighlighted:newValue];
1853 - (BOOL)markedTextMatchesAreHighlighted
1855 return [[self _bridge] markedTextMatchesAreHighlighted];
1858 - (void)unmarkAllTextMatches
1860 return [[self _bridge] unmarkAllTextMatches];
1863 - (NSArray *)rectsForTextMatches
1865 return [[self _bridge] rectsForTextMatches];
1868 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1870 ASSERT([self _hasSelection]);
1871 NSArray *types = [self pasteboardTypesForSelection];
1873 // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1874 NSAttributedString *attributedString = [self selectedAttributedString];
1875 NSMutableArray *mutableTypes = nil;
1876 if (![attributedString containsAttachments]) {
1877 mutableTypes = [types mutableCopy];
1878 [mutableTypes removeObject:NSRTFDPboardType];
1879 types = mutableTypes;
1882 [pasteboard declareTypes:types owner:nil];
1883 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1884 [mutableTypes release];
1889 if (_private->closed)
1891 [self _clearLastHitViewIfSelf];
1892 // FIXME: This is slow; should remove individual observers instead.
1893 [[NSNotificationCenter defaultCenter] removeObserver:self];
1894 [_private->pluginController destroyAllPlugins];
1895 // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1896 [self removeAllToolTips];
1897 _private->closed = YES;
1902 @implementation NSView (WebHTMLViewFileInternal)
1904 - (void)_web_setPrintingModeRecursive
1906 [_subviews makeObjectsPerformSelector:@selector(_web_setPrintingModeRecursive)];
1909 - (void)_web_clearPrintingModeRecursive
1911 [_subviews makeObjectsPerformSelector:@selector(_web_clearPrintingModeRecursive)];
1914 - (void)_web_layoutIfNeededRecursive
1916 [_subviews makeObjectsPerformSelector:@selector(_web_layoutIfNeededRecursive)];
1919 - (void)_web_layoutIfNeededRecursive: (NSRect)rect testDirtyRect:(bool)testDirtyRect
1921 unsigned index, count;
1922 for (index = 0, count = [(NSArray *)_subviews count]; index < count; index++) {
1923 NSView *subview = [_subviews objectAtIndex:index];
1924 NSRect dirtiedSubviewRect = [subview convertRect: rect fromView: self];
1925 [subview _web_layoutIfNeededRecursive: dirtiedSubviewRect testDirtyRect:testDirtyRect];
1931 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
1933 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
1935 if (object == nil) {
1936 [self removeObjectForKey:key];
1938 [self setObject:object forKey:key];
1944 // The following is a workaround for
1945 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
1946 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
1947 // Since the category will be searched before the real class, we'll prevent the flag from being
1948 // set on the tool tip panel.
1950 @interface NSToolTipPanel : NSPanel
1953 @interface NSToolTipPanel (WebHTMLViewFileInternal)
1956 @implementation NSToolTipPanel (WebHTMLViewFileInternal)
1958 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
1960 // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
1965 @interface NSArray (WebHTMLView)
1966 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
1969 @implementation WebHTMLView
1973 [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes]
1974 returnTypes:[[self class] _insertablePasteboardTypes]];
1975 _NSInitializeKillRing();
1978 - (void)_resetCachedWebPreferences:(NSNotification *)ignored
1980 WebPreferences *preferences = [[self _webView] preferences];
1981 // Check for nil because we might not yet have an associated webView when this is called
1982 if (preferences == nil) {
1983 preferences = [WebPreferences standardPreferences];
1985 _private->showsURLsInToolTips = [preferences showsURLsInToolTips];
1988 - (id)initWithFrame:(NSRect)frame
1990 self = [super initWithFrame:frame];
1994 // Make all drawing go through us instead of subviews.
1995 if (NSAppKitVersionNumber >= 711) {
1996 [self _setDrawsOwnDescendants:YES];
1999 _private = [[WebHTMLViewPrivate alloc] init];
2001 _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2002 _private->needsLayout = YES;
2003 [self _resetCachedWebPreferences:nil];
2004 [[NSNotificationCenter defaultCenter]
2005 addObserver:self selector:@selector(_resetCachedWebPreferences:)
2006 name:WebPreferencesChangedNotification object:nil];
2013 // We can't assert that close has already been called because
2014 // this view can be removed from it's superview, even though
2015 // it could be needed later, so close if needed.
2024 // We can't assert that close has already been called because
2025 // this view can be removed from it's superview, even though
2026 // it could be needed later, so close if needed.
2031 - (IBAction)takeFindStringFromSelection:(id)sender
2033 if (![self _hasSelection]) {
2038 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2041 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2043 [pasteboard declareTypes:types owner:nil];
2044 [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2048 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2050 if ([[self _bridge] isSelectionRichlyEditable])
2051 [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
2053 [self _pasteAsPlainTextWithPasteboard:pasteboard];
2057 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2059 if (sendType != nil && [[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]) {
2061 } else if (returnType != nil && [[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2064 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2067 - (void)selectAll:(id)sender
2072 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2073 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will us the
2074 // selector centerSelectionInVisibleArea. We'll leave this old selector in place for two reasons:
2075 // (1) compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2076 // might be using the jumpToSelection: selector, and we don't want to break them.
2077 - (void)jumpToSelection:(id)sender
2079 [self centerSelectionInVisibleArea:sender];
2082 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2084 SEL action = [item action];
2085 WebFrameBridge *bridge = [self _bridge];
2087 if (action == @selector(changeSpelling:)
2088 || action == @selector(_changeSpellingFromMenu:)
2089 || action == @selector(checkSpelling:)
2090 || action == @selector(complete:)
2091 || action == @selector(deleteBackward:)
2092 || action == @selector(deleteBackwardByDecomposingPreviousCharacter:)
2093 || action == @selector(deleteForward:)
2094 || action == @selector(deleteToBeginningOfLine:)
2095 || action == @selector(deleteToBeginningOfParagraph:)
2096 || action == @selector(deleteToEndOfLine:)
2097 || action == @selector(deleteToEndOfParagraph:)
2098 || action == @selector(deleteToMark:)
2099 || action == @selector(deleteWordBackward:)
2100 || action == @selector(deleteWordForward:)
2101 || action == @selector(insertBacktab:)
2102 || action == @selector(insertLineBreak:)
2103 || action == @selector(insertNewline:)
2104 || action == @selector(insertNewlineIgnoringFieldEditor:)
2105 || action == @selector(insertParagraphSeparator:)
2106 || action == @selector(insertTab:)
2107 || action == @selector(insertTabIgnoringFieldEditor:)
2108 || action == @selector(moveBackward:)
2109 || action == @selector(moveBackwardAndModifySelection:)
2110 || action == @selector(moveDown:)
2111 || action == @selector(moveDownAndModifySelection:)
2112 || action == @selector(moveForward:)
2113 || action == @selector(moveForwardAndModifySelection:)
2114 || action == @selector(moveLeft:)
2115 || action == @selector(moveLeftAndModifySelection:)
2116 || action == @selector(moveParagraphBackwardAndModifySelection:)
2117 || action == @selector(moveParagraphForwardAndModifySelection:)
2118 || action == @selector(moveRight:)
2119 || action == @selector(moveRightAndModifySelection:)
2120 || action == @selector(moveToBeginningOfDocument:)
2121 || action == @selector(moveToBeginningOfDocumentAndModifySelection:)
2122 || action == @selector(moveToBeginningOfSentence:)
2123 || action == @selector(moveToBeginningOfSentenceAndModifySelection:)
2124 || action == @selector(moveToBeginningOfLine:)
2125 || action == @selector(moveToBeginningOfLineAndModifySelection:)
2126 || action == @selector(moveToBeginningOfParagraph:)
2127 || action == @selector(moveToBeginningOfParagraphAndModifySelection:)
2128 || action == @selector(moveToEndOfDocument:)
2129 || action == @selector(moveToEndOfDocumentAndModifySelection:)
2130 || action == @selector(moveToEndOfSentence:)
2131 || action == @selector(moveToEndOfSentenceAndModifySelection:)
2132 || action == @selector(moveToEndOfLine:)
2133 || action == @selector(moveToEndOfLineAndModifySelection:)
2134 || action == @selector(moveToEndOfParagraph:)
2135 || action == @selector(moveToEndOfParagraphAndModifySelection:)
2136 || action == @selector(moveUp:)
2137 || action == @selector(moveUpAndModifySelection:)
2138 || action == @selector(moveWordBackward:)
2139 || action == @selector(moveWordBackwardAndModifySelection:)
2140 || action == @selector(moveWordForward:)
2141 || action == @selector(moveWordForwardAndModifySelection:)
2142 || action == @selector(moveWordLeft:)
2143 || action == @selector(moveWordLeftAndModifySelection:)
2144 || action == @selector(moveWordRight:)
2145 || action == @selector(moveWordRightAndModifySelection:)
2146 || action == @selector(pageDown:)
2147 || action == @selector(pageDownAndModifySelection:)
2148 || action == @selector(pageUp:)
2149 || action == @selector(pageUpAndModifySelection:)
2150 || action == @selector(pasteFont:)
2151 || action == @selector(showGuessPanel:)
2152 || action == @selector(transpose:)
2153 || action == @selector(yank:)
2154 || action == @selector(yankAndSelect:)) {
2155 return [self _canEdit];
2156 } else if (action == @selector(changeBaseWritingDirection:)) {
2157 NSMenuItem *menuItem = (NSMenuItem *)item;
2158 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2159 NSWritingDirection writingDirection = [item tag];
2160 if (writingDirection == NSWritingDirectionNatural) {
2161 [menuItem setState:NO];
2164 DOMCSSStyleDeclaration* style = [self _emptyStyle];
2165 [style setDirection:writingDirection == NSWritingDirectionLeftToRight ? @"LTR" : @"RTL"];
2166 [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2168 return [self _canEdit];
2169 } else if (action == @selector(toggleBaseWritingDirection:)) {
2170 NSMenuItem *menuItem = (NSMenuItem *)item;
2171 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2172 DOMCSSStyleDeclaration* rtl = [self _emptyStyle];
2173 [rtl setDirection:@"RTL"];
2174 // Take control of the title of the menu item, instead of just checking/unchecking it because otherwise
2175 // we don't know what the check would mean.
2176 [menuItem setTitle:[[self _bridge] selectionHasStyle:rtl] ? UI_STRING("Left to Right", "Left to Right context menu item") : UI_STRING("Right to Left", "Right to Left context menu item")];
2178 return [self _canEdit];
2179 } else if (action == @selector(alignCenter:)
2180 || action == @selector(alignLeft:)
2181 || action == @selector(alignJustified:)
2182 || action == @selector(alignRight:)
2183 || action == @selector(changeAttributes:)
2184 || action == @selector(changeColor:)
2185 || action == @selector(changeFont:)) {
2186 return [self _canEditRichly];
2187 } else if (action == @selector(capitalizeWord:)
2188 || action == @selector(lowercaseWord:)
2189 || action == @selector(uppercaseWord:)) {
2190 return [self _hasSelection] && [self _isEditable];
2191 } else if (action == @selector(centerSelectionInVisibleArea:)
2192 || action == @selector(jumpToSelection:)
2193 || action == @selector(copyFont:)
2194 || action == @selector(setMark:)) {
2195 return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2196 } else if (action == @selector(changeDocumentBackgroundColor:)) {
2197 return [[self _webView] isEditable] && [self _canEditRichly];
2198 } else if (action == @selector(copy:)) {
2199 return [bridge mayDHTMLCopy] || [self _canCopy];
2200 } else if (action == @selector(cut:)) {
2201 return [bridge mayDHTMLCut] || [self _canCut];
2202 } else if (action == @selector(delete:)) {
2203 return [self _canDelete];
2204 } else if (action == @selector(_ignoreSpellingFromMenu:)
2205 || action == @selector(_learnSpellingFromMenu:)
2206 || action == @selector(takeFindStringFromSelection:)) {
2207 return [self _hasSelection];
2208 } else if (action == @selector(paste:) || action == @selector(pasteAsPlainText:)) {
2209 return [bridge mayDHTMLPaste] || [self _canPaste];
2210 } else if (action == @selector(pasteAsRichText:)) {
2211 return [bridge mayDHTMLPaste] || ([self _canPaste] && [[self _bridge] isSelectionRichlyEditable]);
2212 } else if (action == @selector(performFindPanelAction:)) {
2213 // FIXME: Not yet implemented.
2215 } else if (action == @selector(selectToMark:)
2216 || action == @selector(swapWithMark:)) {
2217 return [self _hasSelectionOrInsertionPoint] && [[self _bridge] markDOMRange] != nil;
2218 } else if (action == @selector(subscript:)) {
2219 NSMenuItem *menuItem = (NSMenuItem *)item;
2220 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2221 DOMCSSStyleDeclaration *style = [self _emptyStyle];
2222 [style setVerticalAlign:@"sub"];
2223 [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2225 return [self _canEditRichly];
2226 } else if (action == @selector(superscript:)) {
2227 NSMenuItem *menuItem = (NSMenuItem *)item;
2228 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2229 DOMCSSStyleDeclaration *style = [self _emptyStyle];
2230 [style setVerticalAlign:@"super"];
2231 [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2233 return [self _canEditRichly];
2234 } else if (action == @selector(underline:)) {
2235 NSMenuItem *menuItem = (NSMenuItem *)item;
2236 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2237 DOMCSSStyleDeclaration *style = [self _emptyStyle];
2238 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
2239 [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2241 return [self _canEditRichly];
2242 } else if (action == @selector(unscript:)) {
2243 NSMenuItem *menuItem = (NSMenuItem *)item;
2244 if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2245 DOMCSSStyleDeclaration *style = [self _emptyStyle];
2246 [style setVerticalAlign:@"baseline"];
2247 [menuItem setState:[[self _bridge] selectionHasStyle:style]];
2249 return [self _canEditRichly];
2250 } else if (action == @selector(_lookUpInDictionaryFromMenu:)) {
2251 return [self _hasSelection];
2257 - (BOOL)acceptsFirstResponder
2259 // Don't accept first responder when we first click on this view.
2260 // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2261 // Do accept first responder at any other time, for example from keyboard events,
2262 // or from calls back from WebCore once we begin mouse-down event handling.
2263 NSEvent *event = [NSApp currentEvent];
2264 if ([event type] == NSLeftMouseDown
2265 && !_private->handlingMouseDownEvent
2266 && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2272 - (BOOL)maintainsInactiveSelection
2274 // This method helps to determine whether the WebHTMLView should maintain
2275 // an inactive selection when it's not first responder.
2276 // Traditionally, these views have not maintained such selections,
2277 // clearing them when the view was not first responder. However,
2278 // to fix bugs like this one:
2279 // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even
2280 // when they're not firstResponder"
2281 // it was decided to add a switch to act more like an NSTextView.
2282 id nextResponder = [[self window] _newFirstResponderAfterResigning];
2284 // Predict the case where we are losing first responder status only to
2285 // gain it back again. Want to keep the selection in that case.
2286 if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2287 id contentView = [nextResponder contentView];
2289 nextResponder = contentView;
2292 if ([nextResponder isKindOfClass:[NSClipView class]]) {
2293 id documentView = [nextResponder documentView];
2295 nextResponder = documentView;
2299 if (nextResponder == self)
2302 BOOL nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]] && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2304 return [[self _webView] maintainsInactiveSelection] || ([[self _bridge] isSelectionEditable] && nextResponderIsInWebView);
2307 - (void)addMouseMovedObserver
2309 if (!_private->dataSource || ![self _isTopHTMLView])
2312 // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2313 if (!([[self window] isKeyWindow] || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]))
2316 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2317 name:WKMouseMovedNotification() object:nil];
2318 [self _frameOrBoundsChanged];
2321 - (void)removeMouseMovedObserver
2323 // Don't remove the observer if we're running the Dashboard.
2324 // FIXME: Right for the windowDidResignKey: case, but wrong for the viewWillMoveToWindow: case.
2325 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2328 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2329 [[NSNotificationCenter defaultCenter] removeObserver:self
2330 name:WKMouseMovedNotification() object:nil];
2333 - (void)addSuperviewObservers
2335 // We watch the bounds of our superview, so that we can do a layout when the size
2336 // of the superview changes. This is different from other scrollable things that don't
2337 // need this kind of thing because their layout doesn't change.
2339 // We need to pay attention to both height and width because our "layout" has to change
2340 // to extend the background the full height of the space and because some elements have
2341 // sizes that are based on the total size of the view.
2343 NSView *superview = [self superview];
2344 if (superview && [self window]) {
2345 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged)
2346 name:NSViewFrameDidChangeNotification object:superview];
2347 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged)
2348 name:NSViewBoundsDidChangeNotification object:superview];
2350 // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2351 // It will check the current size/scroll against the previous layout's size/scroll. We need to
2352 // do this here to catch the case where the WebView is laid out at one size, removed from its
2353 // window, resized, and inserted into another window. Our frame/bounds changed notifications
2354 // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2355 [self _frameOrBoundsChanged];
2359 - (void)removeSuperviewObservers
2361 NSView *superview = [self superview];
2362 if (superview && [self window]) {
2363 [[NSNotificationCenter defaultCenter] removeObserver:self
2364 name:NSViewFrameDidChangeNotification object:superview];
2365 [[NSNotificationCenter defaultCenter] removeObserver:self
2366 name:NSViewBoundsDidChangeNotification object:superview];
2370 - (void)addWindowObservers
2372 NSWindow *window = [self window];
2374 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:)
2375 name:NSWindowDidBecomeKeyNotification object:nil];
2376 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:)
2377 name:NSWindowDidResignKeyNotification object:nil];
2378 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:)
2379 name:NSWindowWillCloseNotification object:window];
2383 - (void)removeWindowObservers
2385 NSWindow *window = [self window];
2387 [[NSNotificationCenter defaultCenter] removeObserver:self
2388 name:NSWindowDidBecomeKeyNotification object:nil];
2389 [[NSNotificationCenter defaultCenter] removeObserver:self
2390 name:NSWindowDidResignKeyNotification object:nil];
2391 [[NSNotificationCenter defaultCenter] removeObserver:self
2392 name:NSWindowWillCloseNotification object:window];
2396 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2398 [self removeSuperviewObservers];
2401 - (void)viewDidMoveToSuperview
2403 // Do this here in case the text size multiplier changed when a non-HTML
2404 // view was installed.
2405 if ([self superview] != nil) {
2406 [self _updateTextSizeMultiplier];
2407 [self addSuperviewObservers];
2411 - (void)viewWillMoveToWindow:(NSWindow *)window
2413 // Don't do anything if we aren't initialized. This happens
2414 // when decoding a WebView. When WebViews are decoded their subviews
2415 // are created by initWithCoder: and so won't be normally
2416 // initialized. The stub views are discarded by WebView.
2418 // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2419 [self removeMouseMovedObserver];
2420 [self removeWindowObservers];
2421 [self removeSuperviewObservers];
2422 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
2424 [[self _pluginController] stopAllPlugins];
2428 - (void)viewDidMoveToWindow
2430 // Don't do anything if we aren't initialized. This happens
2431 // when decoding a WebView. When WebViews are decoded their subviews
2432 // are created by initWithCoder: and so won't be normally
2433 // initialized. The stub views are discarded by WebView.
2435 [self _stopAutoscrollTimer];
2436 if ([self window]) {
2437 _private->lastScrollPosition = [[self superview] bounds].origin;
2438 [self addWindowObservers];
2439 [self addSuperviewObservers];
2440 [self addMouseMovedObserver];
2442 // Schedule this update, rather than making the call right now.
2443 // The reason is that placing the caret in the just-installed view requires
2444 // the HTML/XML document to be available on the WebCore side, but it is not
2445 // at the time this code is running. However, it will be there on the next
2446 // crank of the run loop. Doing this helps to make a blinking caret appear
2447 // in a new, empty window "automatic".
2448 [self performSelector:@selector(_updateActiveState) withObject:nil afterDelay:0];
2450 [[self _pluginController] startAllPlugins];
2452 _private->lastScrollPosition = NSZeroPoint;
2457 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2459 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2462 - (void)viewDidMoveToHostWindow
2464 [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2468 - (void)addSubview:(NSView *)view
2470 [super addSubview:view];
2472 if ([WebPluginController isPlugInView:view]) {
2473 [[self _pluginController] addPlugin:view];
2477 - (void)willRemoveSubview:(NSView *)subview
2479 if ([WebPluginController isPlugInView:subview])
2480 [[self _pluginController] destroyPlugin:subview];
2481 [super willRemoveSubview:subview];
2484 - (void)reapplyStyles
2486 if (!_private->needsToApplyStyles) {
2491 double start = CFAbsoluteTimeGetCurrent();
2494 [[self _bridge] reapplyStylesForDeviceType:
2495 _private->printing ? WebCoreDevicePrinter : WebCoreDeviceScreen];
2498 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2499 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2502 _private->needsToApplyStyles = NO;
2505 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2506 // minPageWidth==0 implies a non-printing layout
2507 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
2509 [self reapplyStyles];
2511 // Ensure that we will receive mouse move events. Is this the best place to put this?
2512 [[self window] setAcceptsMouseMovedEvents: YES];
2513 WKSetNSWindowShouldPostEventNotifications([self window], YES);
2515 if (!_private->needsLayout) {
2520 double start = CFAbsoluteTimeGetCurrent();
2523 LOG(View, "%@ doing layout", self);
2525 if (minPageWidth > 0.0) {
2526 [[self _bridge] forceLayoutWithMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
2528 [[self _bridge] forceLayoutAdjustingViewSize:adjustViewSize];
2530 _private->needsLayout = NO;
2532 if (!_private->printing) {
2533 // get size of the containing dynamic scrollview, so
2534 // appearance and disappearance of scrollbars will not show up
2536 NSSize newLayoutFrameSize = [[[self superview] superview] frame].size;
2537 if (_private->laidOutAtLeastOnce && !NSEqualSizes(_private->lastLayoutFrameSize, newLayoutFrameSize)) {
2538 [[self _bridge] sendResizeEvent];
2539 if ([[self _bridge] needsLayout])
2540 [[self _bridge] forceLayoutAdjustingViewSize:NO];
2542 _private->laidOutAtLeastOnce = YES;
2543 _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
2544 _private->lastLayoutFrameSize = newLayoutFrameSize;
2548 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2549 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
2555 [self layoutToMinimumPageWidth:0.0f maximumPageWidth:0.0f adjustingViewSize:NO];
2558 - (NSMenu *)menuForEvent:(NSEvent *)event
2560 [_private->compController endRevertingChange:NO moveLeft:NO];
2562 _private->handlingMouseDownEvent = YES;
2563 BOOL handledEvent = [[self _bridge] sendContextMenuEvent:event];
2564 _private->handlingMouseDownEvent = NO;
2568 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2569 NSDictionary *element = [self elementAtPoint:point];
2570 return [[self _webView] _menuForElement:element defaultItems:nil];
2573 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
2575 if (![string length])
2578 return [[self _bridge] searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag];
2581 - (void)deselectText
2583 [[self _bridge] deselectText];
2588 return [[self _webView] drawsBackground];
2591 - (void)setNeedsDisplay:(BOOL)flag
2593 LOG(View, "%@ flag = %d", self, (int)flag);
2594 [super setNeedsDisplay: flag];
2597 - (void)setNeedsLayout: (BOOL)flag
2599 LOG(View, "%@ flag = %d", self, (int)flag);
2600 _private->needsLayout = flag;
2604 - (void)setNeedsToApplyStyles: (BOOL)flag
2606 LOG(View, "%@ flag = %d", self, (int)flag);
2607 _private->needsToApplyStyles = flag;
2610 - (void)drawSingleRect:(NSRect)rect
2612 [NSGraphicsContext saveGraphicsState];
2615 ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
2617 [(WebClipView *)[self superview] setAdditionalClip:rect];
2620 if ([self _transparentBackground]) {
2621 [[NSColor clearColor] set];
2625 [[self _bridge] drawRect:rect];
2626 WebView *webView = [self _webView];
2627 [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
2628 [(WebClipView *)[self superview] resetAdditionalClip];
2630 [NSGraphicsContext restoreGraphicsState];
2632 [(WebClipView *)[self superview] resetAdditionalClip];
2633 [NSGraphicsContext restoreGraphicsState];
2634 LOG_ERROR("Exception caught while drawing: %@", localException);
2635 [localException raise];
2639 - (void)drawRect:(NSRect)rect
2641 LOG(View, "%@ drawing", self);
2643 const NSRect *rects;
2645 [self getRectsBeingDrawn:&rects count:&count];
2647 BOOL subviewsWereSetAside = _private->subviewsSetAside;
2648 if (subviewsWereSetAside)
2649 [self _restoreSubviews];
2652 double start = CFAbsoluteTimeGetCurrent();
2655 // If count == 0 here, use the rect passed in for drawing. This is a workaround for:
2656 // <rdar://problem/3908282> REGRESSION (Mail): No drag image dragging selected text in Blot and Mail
2657 // The reason for the workaround is that this method is called explicitly from the code
2658 // to generate a drag image, and at that time, getRectsBeingDrawn:count: will return a zero count.
2659 const int cRectThreshold = 10;
2660 const float cWastedSpaceThreshold = 0.75f;
2661 BOOL useUnionedRect = (count <= 1) || (count > cRectThreshold);
2662 if (!useUnionedRect) {
2663 // Attempt to guess whether or not we should use the unioned rect or the individual rects.
2664 // We do this by computing the percentage of "wasted space" in the union. If that wasted space
2665 // is too large, then we will do individual rect painting instead.
2666 float unionPixels = (rect.size.width * rect.size.height);
2667 float singlePixels = 0;
2668 for (int i = 0; i < count; ++i)
2669 singlePixels += rects[i].size.width * rects[i].size.height;
2670 float wastedSpace = 1 - (singlePixels / unionPixels);
2671 if (wastedSpace <= cWastedSpaceThreshold)
2672 useUnionedRect = YES;
2676 [self drawSingleRect:rect];
2678 for (int i = 0; i < count; ++i)
2679 [self drawSingleRect:rects[i]];
2682 double thisTime = CFAbsoluteTimeGetCurrent() - start;
2683 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
2686 if (subviewsWereSetAside)
2687 [self _setAsideSubviews];
2690 // Turn off the additional clip while computing our visibleRect.
2691 - (NSRect)visibleRect
2693 if (!([[self superview] isKindOfClass:[WebClipView class]]))
2694 return [super visibleRect];
2696 WebClipView *clipView = (WebClipView *)[self superview];
2698 BOOL hasAdditionalClip = [clipView hasAdditionalClip];
2699 if (!hasAdditionalClip) {
2700 return [super visibleRect];
2703 NSRect additionalClip = [clipView additionalClip];
2704 [clipView resetAdditionalClip];
2705 NSRect visibleRect = [super visibleRect];
2706 [clipView setAdditionalClip:additionalClip];
2715 - (void)windowDidBecomeKey:(NSNotification *)notification
2717 NSWindow *keyWindow = [notification object];
2719 if (keyWindow == [self window])
2720 [self addMouseMovedObserver];
2722 if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
2723 [self _updateActiveState];
2726 - (void)windowDidResignKey:(NSNotification *)notification
2728 NSWindow *formerKeyWindow = [notification object];
2730 if (formerKeyWindow == [self window])
2731 [self removeMouseMovedObserver];
2733 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
2734 [self _updateActiveState];
2735 [_private->compController endRevertingChange:NO moveLeft:NO];
2739 - (void)windowWillClose:(NSNotification *)notification
2741 [_private->compController endRevertingChange:NO moveLeft:NO];
2742 [[self _pluginController] destroyAllPlugins];
2745 - (void)scrollWheel:(NSEvent *)event
2749 if (![[self _bridge] sendScrollWheelEvent:event]) {
2750 [[self nextResponder] scrollWheel:event];
2756 - (BOOL)_isSelectionEvent:(NSEvent *)event
2758 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2759 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
2762 - (BOOL)acceptsFirstMouse:(NSEvent *)event
2764 NSView *hitView = [self _hitViewForEvent:event];
2765 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2767 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
2770 if (hitHTMLView != nil) {
2771 [hitHTMLView _setMouseDownEvent:event];
2772 [[hitHTMLView _bridge] setActivationEventNumber:[event eventNumber]];
2773 BOOL result = [hitHTMLView _isSelectionEvent:event] ? [[hitHTMLView _bridge] eventMayStartDrag:event] : NO;
2774 [hitHTMLView _setMouseDownEvent:nil];
2777 return [hitView acceptsFirstMouse:event];
2780 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
2782 NSView *hitView = [self _hitViewForEvent:event];
2783 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2784 if (hitHTMLView != nil) {
2785 [hitHTMLView _setMouseDownEvent:event];
2786 BOOL result = [hitHTMLView _isSelectionEvent:event] ? [[hitHTMLView _bridge] eventMayStartDrag:event] : NO;
2787 [hitHTMLView _setMouseDownEvent:nil];
2790 return [hitView shouldDelayWindowOrderingForEvent:event];
2793 - (void)mouseDown:(NSEvent *)event
2797 _private->handlingMouseDownEvent = YES;
2799 // Record the mouse down position so we can determine drag hysteresis.
2800 [self _setMouseDownEvent:event];
2802 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2803 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2806 [_private->compController endRevertingChange:NO moveLeft:NO];
2808 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
2809 // We don't want to pass them along to KHTML a second time.
2810 if (!([event modifierFlags] & NSControlKeyMask)) {
2811 _private->ignoringMouseDraggedEvents = NO;
2813 // Don't do any mouseover while the mouse is down.
2814 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_updateMouseoverWithFakeEvent) object:nil];
2816 // Let KHTML get a chance to deal with the event. This will call back to us
2817 // to start the autoscroll timer if appropriate.
2818 [[self _bridge] mouseDown:event];
2822 [_private->firstResponderTextViewAtMouseDownTime release];
2823 _private->firstResponderTextViewAtMouseDownTime = nil;
2825 _private->handlingMouseDownEvent = NO;
2830 - (void)dragImage:(NSImage *)dragImage
2832 offset:(NSSize)offset
2833 event:(NSEvent *)event
2834 pasteboard:(NSPasteboard *)pasteboard
2836 slideBack:(BOOL)slideBack
2838 [self _stopAutoscrollTimer];
2840 WebHTMLView *topHTMLView = [self _topHTMLView];
2841 if (self != topHTMLView) {
2842 [topHTMLView dragImage:dragImage at:[self convertPoint:at toView:topHTMLView]
2843 offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
2847 WebView *webView = [self _webView];
2849 [webView _setInitiatedDrag:YES];
2851 // Retain this view during the drag because it may be released before the drag ends.
2854 id UIDelegate = [webView UIDelegate];
2855 // If a delegate takes over the drag but never calls draggedImage: endedAt:, we'll leak the WebHTMLView.
2856 if ([UIDelegate respondsToSelector:@selector(webView:dragImage:at:offset:event:pasteboard:source:slideBack:forView:)])
2857 [UIDelegate webView:webView dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack forView:self];
2859 [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
2862 - (void)mouseDragged:(NSEvent *)event
2864 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2865 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2870 if (!_private->ignoringMouseDraggedEvents)
2871 [[self _bridge] mouseDragged:event];
2876 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
2878 ASSERT([self _isTopHTMLView]);
2880 if (_private->webCoreDragOp == NSDragOperationNone)
2881 return NSDragOperationGeneric | NSDragOperationCopy;
2882 return _private->webCoreDragOp;
2885 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
2887 ASSERT([self _isTopHTMLView]);
2889 NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
2890 NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + _private->dragOffset.x, windowImageLoc.y + _private->dragOffset.y);
2891 [[self _bridge] dragSourceMovedTo:windowMouseLoc];
2894 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
2896 ASSERT(![self _webView] || [self _isTopHTMLView]);
2898 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
2899 NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + _private->dragOffset.x, windowImageLoc.y + _private->dragOffset.y);
2900 [[self _bridge] dragSourceEndedAt:windowMouseLoc operation:operation];
2902 _private->initiatedDrag = NO;
2903 [[self _webView] _setInitiatedDrag:NO];
2905 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
2906 _private->ignoringMouseDraggedEvents = YES;
2908 // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
2909 // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
2910 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2911 location:windowMouseLoc
2912 modifierFlags:[[NSApp currentEvent] modifierFlags]
2913 timestamp:[NSDate timeIntervalSinceReferenceDate]
2914 windowNumber:[[self window] windowNumber]
2915 context:[[NSApp currentEvent] context]
2916 eventNumber:0 clickCount:0 pressure:0];
2917 [self mouseUp:fakeEvent]; // This will also update the mouseover state.
2919 // Balance the previous retain from when the drag started.
2923 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
2925 ASSERT([self _isTopHTMLView]);
2926 ASSERT(_private->draggingImageURL);
2928 NSFileWrapper *wrapper = [[self _dataSource] _fileWrapperForURL:_private->draggingImageURL];
2929 if (wrapper == nil) {
2930 LOG_ERROR("Failed to create image file. Did the source image change while dragging? (<rdar://problem/4244861>)");
2934 // FIXME: Report an error if we fail to create a file.
2935 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
2936 path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
2937 if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
2938 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
2940 return [NSArray arrayWithObject:[path lastPathComponent]];
2943 - (void)mouseUp:(NSEvent *)event
2945 [self _setMouseDownEvent:nil];
2947 NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2948 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2953 [self _stopAutoscrollTimer];
2954 [[self _bridge] mouseUp:event];
2955 [self _updateMouseoverWithFakeEvent];
2960 - (void)mouseMovedNotification:(NSNotification *)notification
2962 [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
2965 // returning YES from this method is the way we tell AppKit that it is ok for this view
2966 // to be in the key loop even when "tab to all controls" is not on.
2967 - (BOOL)needsPanelToBecomeKey
2972 - (NSView *)nextValidKeyView
2975 BOOL lookInsideWebFrameViews = YES;
2976 if ([self isHiddenOrHasHiddenAncestor]) {
2977 lookInsideWebFrameViews = NO;
2978 } else if ([self _frame] == [[self _webView] mainFrame]) {
2979 // Check for case where first responder is last frame in a frameset, and we are
2980 // the top-level documentView.
2981 NSResponder *firstResponder = [[self window] firstResponder];
2982 if ((firstResponder != self) && [firstResponder isKindOfClass:[WebHTMLView class]] && ([(NSView *)firstResponder nextKeyView] == nil)) {
2983 lookInsideWebFrameViews = NO;
2987 if (lookInsideWebFrameViews) {
2988 view = [[self _bridge] nextKeyViewInsideWebFrameViews];
2992 view = [super nextValidKeyView];
2993 // If there's no next view wired up, we must be in the last subframe, or we are
2994 // being called at an unusual time when the views have not yet been wired together.
2995 // There's no direct link to the next valid key view; get it from the bridge.
2996 // Note that view == self here when nextKeyView returns nil, due to AppKit oddness.
2997 // We'll check for both nil and self in case the AppKit oddness goes away.
2998 // WebFrameView has this same kind of logic for the previousValidKeyView case.
2999 if (view == nil || view == self) {
3000 view = [[self _bridge] nextValidKeyViewOutsideWebFrameViews];
3007 - (NSView *)previousValidKeyView
3010 if (![self isHiddenOrHasHiddenAncestor])
3011 view = [[self _bridge] previousKeyViewInsideWebFrameViews];
3013 view = [super previousValidKeyView];
3017 - (BOOL)becomeFirstResponder
3020 if (![[self _webView] _isPerformingProgrammaticFocus] && !_private->willBecomeFirstResponderForNodeFocus) {
3021 switch ([[self window] keyViewSelectionDirection]) {
3022 case NSDirectSelection:
3024 case NSSelectingNext:
3025 view = [[self _bridge] nextKeyViewInsideWebFrameViews];
3027 case NSSelectingPrevious:
3028 view = [[self _bridge] previousKeyViewInsideWebFrameViews];
3032 _private->willBecomeFirstResponderForNodeFocus = NO;
3034 [[self window] makeFirstResponder:view];
3035 [self _updateActiveState];
3036 [self _updateFontPanel];
3037 _private->startNewKillRingSequence = YES;
3041 - (BOOL)resignFirstResponder
3043 BOOL resign = [super resignFirstResponder];
3045 [_private->compController endRevertingChange:NO moveLeft:NO];
3046 _private->resigningFirstResponder = YES;
3047 if (![self maintainsInactiveSelection]) {
3048 if ([[self _webView] _isPerformingProgrammaticFocus])
3049 [self deselectText];
3053 [self _updateActiveState];
3054 _private->resigningFirstResponder = NO;
3055 _private->willBecomeFirstResponderForNodeFocus = NO;
3060 - (void)setDataSource:(WebDataSource *)dataSource
3062 ASSERT(!_private->dataSource);
3063 _private->dataSource = [dataSource retain];
3064 [_private->pluginController setDataSource:dataSource];
3065 [self addMouseMovedObserver];
3068 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3072 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3073 // key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3074 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3075 - (void)updateCell:(NSCell*)cell
3079 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3080 // pageWidth != 0 implies we will relayout to a new width
3081 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
3083 WebFrame *frame = [self _frame];
3084 NSArray *subframes = [frame childFrames];
3085 unsigned n = [subframes count];
3087 for (i = 0; i != n; ++i) {
3088 WebFrame *subframe = [subframes objectAtIndex:i];
3089 WebFrameView *frameView = [subframe frameView];
3090 if ([[subframe dataSource] _isDocumentHTML]) {
3091 [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:adjustViewSize];
3095 if (printing != _private->printing) {
3096 [_private->pageRects release];
3097 _private->pageRects = nil;
3098 _private->printing = printing;
3099 [self setNeedsToApplyStyles:YES];
3100 [self setNeedsLayout:YES];
3101 [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
3103 // Can't do this when starting printing or nested printing won't work, see 3491427.
3104 [self setNeedsDisplay:NO];
3109 - (BOOL)canPrintHeadersAndFooters
3114 // This is needed for the case where the webview is embedded in the view that's being printed.
3115 // It shouldn't be called when the webview is being printed directly.
3116 - (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit
3118 // This helps when we print as part of a larger print process.
3119 // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3120 BOOL wasInPrintingMode = _private->printing;
3121 if (!wasInPrintingMode)
3122 [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3124 [[self _bridge] adjustPageHeightNew:newBottom top:oldTop bottom:oldBottom limit:bottomLimit];
3126 if (!wasInPrintingMode)
3127 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3130 - (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
3132 NSPrintInfo *printInfo = [printOperation printInfo];
3133 return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
3136 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3138 float viewWidth = NSWidth([self bounds]);
3139 if (viewWidth < 1) {
3140 LOG_ERROR("%@ has no width when printing", self);
3144 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3145 float maxShrinkToFitScaleFactor = 1.0f / PrintingMaximumShrinkFactor;
3146 float shrinkToFitScaleFactor = [self _availablePaperWidthForPrintOperation:printOperation]/viewWidth;
3147 return userScaleFactor * MAX(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor);
3150 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3151 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3152 // if AppKit makes it SPI/API.
3153 - (float)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3155 return [self _scaleFactorForPrintOperation:printOperation];
3158 // This is used for Carbon printing. At some point we might want to make this public API.
3159 - (void)setPageWidthForPrinting:(float)pageWidth
3161 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3162 [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
3165 - (void)_endPrintMode
3167 [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
3168 [[self window] setAutodisplay:YES];
3171 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3173 ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3174 NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3175 if (initiatingOperation == currentOperation) {
3176 // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3177 // being extra paranoid here since the printing code is so fragile. Delay the cleanup
3179 ASSERT_NOT_REACHED();
3180 [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3181 } else if ([currentOperation view] == self) {
3182 // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3183 // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3184 // fragile. Do nothing, because we don't want to break the print job currently in progress, and
3185 // the print job currently in progress is responsible for its own cleanup.
3186 ASSERT_NOT_REACHED();
3188 // The print job that kicked off this delayed call has finished, and this view is not being
3189 // printed again. We expect that no other print job has started. Since this delayed call wasn't
3190 // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3191 // the print mode here.
3192 ASSERT(currentOperation == nil);
3193 [self _endPrintMode];
3197 // Return the number of pages available for printing
3198 - (BOOL)knowsPageRange:(NSRangePointer)range
3200 // Must do this explicit display here, because otherwise the view might redisplay while the print
3201 // sheet was up, using printer fonts (and looking different).
3202 [self displayIfNeeded];
3203 [[self window] setAutodisplay:NO];
3205 // If we are a frameset just print with the layout we have onscreen, otherwise relayout
3206 // according to the paper size
3207 float minLayoutWidth = 0.0f;
3208 float maxLayoutWidth = 0.0f;
3209 if (![[self _bridge] isFrameSet]) {
3210 float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
3211 minLayoutWidth = paperWidth * PrintingMinimumShrinkFactor;
3212 maxLayoutWidth = paperWidth * PrintingMaximumShrinkFactor;
3214 [self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; // will relayout
3215 NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3216 // Certain types of errors, including invalid page ranges, can cause beginDocument and
3217 // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3218 // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3219 // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3220 // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3221 // which is after beginDocument and endDocument would be called.
3222 [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3223 [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3225 // There is a theoretical chance that someone could do some drawing between here and endDocument,
3226 // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3227 // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3229 range->location = 1;
3230 float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3231 float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3232 [_private->pageRects release];
3233 float fullPageHeight = floorf([self _calculatePrintHeight]/totalScaleFactor);
3234 NSArray *newPageRects = [[self _bridge] computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3235 printHeight:fullPageHeight];
3237 // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3238 // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3239 // a blank page (with correct-looking header and footer if that option is on), which matches
3240 // the behavior of IE and Camino at least.
3241 if ([newPageRects count] == 0)
3242 newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3243 else if ([newPageRects count] > 1) {
3244 // If the last page is a short orphan, try adjusting the print height slightly to see if this will squeeze the
3245 // content onto one fewer page. If it does, use the adjusted scale. If not, use the original scale.
3246 float lastPageHeight = NSHeight([[newPageRects lastObject] rectValue]);
3247 if (lastPageHeight/fullPageHeight < LastPrintedPageOrphanRatio) {
3248 NSArray *adjustedPageRects = [[self _bridge] computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3249 printHeight:fullPageHeight*PrintingOrphanShrinkAdjustment];
3250 // Use the adjusted rects only if the page count went down
3251 if ([adjustedPageRects count] < [newPageRects count])
3252 newPageRects = adjustedPageRects;
3256 _private->pageRects = [newPageRects retain];
3258 range->length = [_private->pageRects count];
3263 // Return the drawing rectangle for a particular page number
3264 - (NSRect)rectForPage:(int)page
3266 return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3269 - (void)drawPageBorderWithSize:(NSSize)borderSize
3271 ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));
3272 [[self _webView] _drawHeaderAndFooter];
3275 - (void)beginDocument
3278 // From now on we'll get a chance to call _endPrintMode in either beginDocument or
3279 // endDocument, so we can cancel the "just in case" pending call.
3280 [NSObject cancelPreviousPerformRequestsWithTarget:self
3281 selector:@selector(_delayedEndPrintMode:)
3282 object:[NSPrintOperation currentOperation]];
3283 [super beginDocument];
3285 // Exception during [super beginDocument] means that endDocument will not get called,
3286 // so we need to clean up our "print mode" here.
3287 [self _endPrintMode];
3293 [super endDocument];
3294 // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO
3295 [self _endPrintMode];
3298 - (BOOL)_interceptEditingKeyEvent:(NSEvent *)event
3300 // Use WebView's tabKeyCyclesThroughElements state to determine whether or not
3301 // to process tab key events. The idea here is that tabKeyCyclesThroughElements
3302 // will be YES when this WebView is being used in a browser, and we desire the
3303 // behavior where tab moves to the next element in tab order. If tabKeyCyclesThroughElements
3304 // is NO, it is likely that the WebView is being embedded as the whole view, as in Mail,
3305 // and tabs should input tabs as expected in a text editor. Using Option-Tab always cycles
3306 // through elements.
3308 if ([[self _webView] tabKeyCyclesThroughElements] && [event _web_isTabKeyEvent])
3311 if (![[self _webView] tabKeyCyclesThroughElements] && [event _web_isOptionTabKeyEvent])
3314 // Now process the key normally
3315 [self interpretKeyEvents:[NSArray arrayWithObject:event]];
3319 - (void)keyDown:(NSEvent *)event
3323 BOOL callSuper = NO;
3325 _private->keyDownEvent = event;
3327 WebFrameBridge *bridge = [self _bridge];
3328 if ([bridge interceptKeyEvent:event toView:self]) {
3329 // WebCore processed a key event, bail on any outstanding complete: UI
3330 [_private->compController endRevertingChange:YES moveLeft:NO];
3331 } else if (_private->compController && [_private->compController filterKeyDown:event]) {
3332 // Consumed by complete: popup window
3334 // We're going to process a key event, bail on any outstanding complete: UI
3335 [_private->compController endRevertingChange:YES moveLeft:NO];
3336 BOOL handledKey = [self _canEdit] && [self _interceptEditingKeyEvent:event];
3341 [super keyDown:event];
3343 [NSCursor setHiddenUntilMouseMoves:YES];
3345 _private->keyDownEvent = nil;
3350 - (void)keyUp:(NSEvent *)event
3353 if (![[self _bridge] interceptKeyEvent:event toView:self])
3354 [super keyUp:event];
3358 - (id)accessibilityAttributeValue:(NSString*)attributeName
3360 if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
3361 id accTree = [[self _bridge] accessibilityTree];
3363 return [NSArray arrayWithObject:accTree];
3366 return [super accessibilityAttributeValue:attributeName];
3369 - (id)accessibilityFocusedUIElement
3371 id accTree = [[self _bridge] accessibilityTree];
3373 return [accTree accessibilityFocusedUIElement];
3377 - (id)accessibilityHitTest:(NSPoint)point
3379 id accTree = [[self _bridge] accessibilityTree];
3381 NSPoint windowCoord = [[self window] convertScreenToBase:point];
3382 return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
3387 - (id)_accessibilityParentForSubview:(NSView *)subview
3389 id accTree = [[self _bridge] accessibilityTree];
3392 id parent = [accTree _accessibilityParentForSubview:subview];
3398 - (void)centerSelectionInVisibleArea:(id)sender
3400 [[self _bridge] centerSelectionInVisibleArea];
3403 - (void)moveBackward:(id)sender
3405 if ([self _canAlterCurrentSelection])
3406 [[self _bridge] alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectByCharacter];
3409 - (void)moveBackwardAndModifySelection:(id)sender
3411 if ([self _canAlterCurrentSelection])
3412 [[self _bridge] alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectByCharacter];
3415 - (void)moveDown:(id)sender
3417 if ([self _canAlterCurrentSelection])
3418 [[self _bridge] alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectByLine];
3421 - (void)moveDownAndModifySelection:(id)sender
3423 if ([self _canAlterCurrentSelection])
3424 [[self _bridge] alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectByLine];
3427 - (void)moveForward:(id)sender
3429 if ([self _canAlterCurrentSelection])
3430 [[self _bridge] alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectForward granularity:WebBridgeSelectByCharacter];
3433 - (void)moveForwardAndModifySelection:(id)sender
3435 if ([self _canAlterCurrentSelection])
3436 [[self _bridge] alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectForward granularity:WebBridgeSelectByCharacter];
3439 - (void)moveLeft:(id)sender
3441 if ([self _canAlterCurrentSelection])
3442 [[self _bridge] alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectLeft granularity:WebBridgeSelectByCharacter];
3445 - (void)moveLeftAndModifySelection:(id)sender
3447 if ([self _canAlterCurrentSelection])
3448 [[self _bridge] alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectLeft granularity:WebBridgeSelectByCharacter];
3451 - (void)moveRight:(id)sender
3453 if ([self _canAlterCurrentSelection])
3454 [[self _bridge] alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectRight granularity:WebBridgeSelectByCharacter];
3457 - (void)moveRightAndModifySelection:(id)sender
3459 if ([self _canAlterCurrentSelection])
3460 [[self _bridge] alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectRight granularity:WebBridgeSelectByCharacter];
3463 - (void)moveToBeginningOfDocument:(id)sender
3465 if ([self _canAlterCurrentSelection])
3466 [[self _bridge] alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToDocumentBoundary];
3469 - (void)moveToBeginningOfDocumentAndModifySelection:(id)sender
3471 if ([self _canAlterCurrentSelection])
3472 [[self _bridge] alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToDocumentBoundary];
3475 - (void)moveToBeginningOfSentence:(id)sender
3477 if ([self _canAlterCurrentSelection])
3478 [[self _bridge] alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToSentenceBoundary];
3481 - (void)moveToBeginningOfSentenceAndModifySelection:(id)sender
3483 if ([self _canAlterCurrentSelection])
3484 [[self _bridge] alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToSentenceBoundary];
3487 - (void)moveToBeginningOfLine:(id)sender
3489 if ([self _canAlterCurrentSelection])
3490 [[self _bridge] alterCurrentSelection:WebSelectByMoving direction:WebBridgeSelectBackward granularity:WebBridgeSelectToLineBoundary];
3493 - (void)moveToBeginningOfLineAndModifySelection:(id)sender
3495 if ([self _canAlterCurrentSelection])
3496 [[self _bridge] alterCurrentSelection:WebSelectByExtending direction:WebBridgeSelectBackward granularity:WebBridgeSelectToLineBoundary];
3499 - (void)moveToBeginningOfParagraph:(id)sender
3501 if ([self _canAlterCurrentSelection])