WebCore:
[WebKit-https.git] / WebKit / mac / WebView / WebHTMLView.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
3  *           (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #import "WebHTMLView.h"
31
32 #import "DOMNodeInternal.h"
33 #import "DOMRangeInternal.h"
34 #import "WebArchive.h"
35 #import "WebArchiver.h"
36 #import "WebBaseNetscapePluginViewInternal.h"
37 #import "WebClipView.h"
38 #import "WebDOMOperationsPrivate.h"
39 #import "WebDataSourceInternal.h"
40 #import "WebDefaultUIDelegate.h"
41 #import "WebDocumentInternal.h"
42 #import "WebDynamicScrollBarsView.h"
43 #import "WebEditingDelegate.h"
44 #import "WebElementDictionary.h"
45 #import "WebFrameBridge.h"
46 #import "WebFrameInternal.h"
47 #import "WebFramePrivate.h"
48 #import "WebFrameViewInternal.h"
49 #import "WebHTMLRepresentationPrivate.h"
50 #import "WebHTMLViewInternal.h"
51 #import "WebKitLogging.h"
52 #import "WebKitNSStringExtras.h"
53 #import "WebKitPluginContainerView.h"
54 #import "WebKitVersionChecks.h"
55 #import "WebLocalizableStrings.h"
56 #import "WebNSAttributedStringExtras.h"
57 #import "WebNSEventExtras.h"
58 #import "WebNSFileManagerExtras.h"
59 #import "WebNSImageExtras.h"
60 #import "WebNSObjectExtras.h"
61 #import "WebNSPasteboardExtras.h"
62 #import "WebNSPrintOperationExtras.h"
63 #import "WebNSURLExtras.h"
64 #import "WebNSViewExtras.h"
65 #import "WebNetscapePluginEmbeddedView.h"
66 #import "WebPluginController.h"
67 #import "WebPreferences.h"
68 #import "WebPreferencesPrivate.h"
69 #import "WebResourcePrivate.h"
70 #import "WebStringTruncator.h"
71 #import "WebUIDelegatePrivate.h"
72 #import "WebViewInternal.h"
73 #import <AppKit/NSAccessibility.h>
74 #import <ApplicationServices/ApplicationServices.h>
75 #import <dlfcn.h>
76 #import <WebCore/CachedImage.h>
77 #import <WebCore/CachedResourceClient.h>
78 #import <WebCore/ColorMac.h>
79 #import <WebCore/ContextMenu.h>
80 #import <WebCore/ContextMenuController.h>
81 #import <WebCore/Document.h>
82 #import <WebCore/Editor.h>
83 #import <WebCore/EditorDeleteAction.h>
84 #import <WebCore/Element.h>
85 #import <WebCore/EventHandler.h>
86 #import <WebCore/EventNames.h>
87 #import <WebCore/ExceptionHandlers.h>
88 #import <WebCore/DragController.h>
89 #import <WebCore/FloatRect.h>
90 #import <WebCore/FocusController.h>
91 #import <WebCore/Frame.h>
92 #import <WebCore/FrameLoader.h>
93 #import <WebCore/FrameView.h>
94 #import <WebCore/HitTestResult.h>
95 #import <WebCore/HTMLNames.h>
96 #import <WebCore/Image.h>
97 #import <WebCore/KeyboardEvent.h>
98 #import <WebCore/MIMETypeRegistry.h>
99 #import <WebCore/Page.h>
100 #import <WebCore/PlatformKeyboardEvent.h>
101 #import <WebCore/PlatformMouseEvent.h>
102 #import <WebCore/Range.h>
103 #import <WebCore/SelectionController.h>
104 #import <WebCore/SharedBuffer.h>
105 #import <WebCore/Text.h>
106 #import <WebCore/WebCoreObjCExtras.h>
107 #import <WebCore/WebCoreTextRenderer.h>
108 #import <WebKit/DOM.h>
109 #import <WebKit/DOMExtensions.h>
110 #import <WebKit/DOMPrivate.h>
111 #import <WebKitSystemInterface.h>
112
113 using namespace WebCore;
114 using namespace HTMLNames;
115 using namespace WTF;
116
117 @interface NSWindow (BorderViewAccess)
118 - (NSView*)_web_borderView;
119 @end
120
121 @implementation NSWindow (BorderViewAccess)
122 - (NSView*)_web_borderView
123 {
124     return _borderView;
125 }
126 @end
127
128 static IMP oldSetCursorIMP = NULL;
129
130 #ifdef BUILDING_ON_TIGER
131 static IMP oldResetCursorRectsIMP = NULL;
132 static BOOL canSetCursor = YES;
133
134 static void resetCursorRects(NSWindow* self, SEL cmd)
135 {
136     NSPoint point = [self mouseLocationOutsideOfEventStream];
137     NSView* view = [[self _web_borderView] hitTest:point];
138     if ([view isKindOfClass:[WebHTMLView class]]) {
139         WebHTMLView *htmlView = (WebHTMLView*)view;
140         NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
141         NSDictionary *dict = [htmlView elementAtPoint:point allowShadowContent:NO];
142         DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
143         if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
144             ![element isKindOfClass:[DOMHTMLEmbedElement class]])
145             canSetCursor = NO;
146     }
147     oldResetCursorRectsIMP(self, cmd);
148     canSetCursor = YES;
149 }
150
151 static void setCursor(NSCursor* self, SEL cmd)
152 {
153     if (canSetCursor)
154         oldSetCursorIMP(self, cmd);
155 }
156 #else
157 static void setCursor(NSWindow* self, SEL cmd, NSPoint point)
158 {
159     NSView* view = [[self _web_borderView] hitTest:point];
160     if ([view isKindOfClass:[WebHTMLView class]]) {
161         WebHTMLView *htmlView = (WebHTMLView*)view;
162         NSPoint localPoint = [htmlView convertPoint:point fromView:nil];
163         NSDictionary *dict = [htmlView elementAtPoint:point allowShadowContent:NO];
164         DOMElement *element = [dict objectForKey:WebElementDOMNodeKey];
165         if (![element isKindOfClass:[DOMHTMLAppletElement class]] && ![element isKindOfClass:[DOMHTMLObjectElement class]] &&
166             ![element isKindOfClass:[DOMHTMLEmbedElement class]])
167             return;
168     }
169     oldSetCursorIMP(self, cmd, point);
170 }
171 #endif
172
173 extern "C" {
174
175 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
176
177 extern NSString *NSMarkedClauseSegmentAttributeName; 
178 extern NSString *NSTextInputReplacementRangeAttributeName; 
179
180 }
181
182 @interface NSView (AppKitSecretsIKnowAbout)
183 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
184 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
185 - (NSRect)_dirtyRect;
186 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
187 - (void)_propagateDirtyRectsToOpaqueAncestors;
188 - (void)_windowChangedKeyState;
189 @end
190
191 @interface NSApplication (AppKitSecretsIKnowAbout)
192 - (void)speakString:(NSString *)string;
193 @end
194
195 @interface NSWindow (AppKitSecretsIKnowAbout)
196 - (id)_newFirstResponderAfterResigning;
197 - (void)_setForceActiveControls:(BOOL)flag;
198 @end
199
200 @interface NSAttributedString (AppKitSecretsIKnowAbout)
201 - (id)_initWithDOMRange:(DOMRange *)domRange;
202 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
203 @end
204
205 @interface NSSpellChecker (CurrentlyPrivateForTextView)
206 - (void)learnWord:(NSString *)word;
207 @end
208
209 // By imaging to a width a little wider than the available pixels,
210 // thin pages will be scaled down a little, matching the way they
211 // print in IE and Camino. This lets them use fewer sheets than they
212 // would otherwise, which is presumably why other browsers do this.
213 // Wide pages will be scaled down more than this.
214 #define PrintingMinimumShrinkFactor     1.25f
215
216 // This number determines how small we are willing to reduce the page content
217 // in order to accommodate the widest line. If the page would have to be
218 // reduced smaller to make the widest line fit, we just clip instead (this
219 // behavior matches MacIE and Mozilla, at least)
220 #define PrintingMaximumShrinkFactor     2.0f
221
222 // This number determines how short the last printed page of a multi-page print session
223 // can be before we try to shrink the scale in order to reduce the number of pages, and
224 // thus eliminate the orphan.
225 #define LastPrintedPageOrphanRatio      0.1f
226
227 // This number determines the amount the scale factor is adjusted to try to eliminate orphans.
228 // It has no direct mathematical relationship to LastPrintedPageOrphanRatio, due to variable
229 // numbers of pages, logic to avoid breaking elements, and CSS-supplied hard page breaks.
230 #define PrintingOrphanShrinkAdjustment  1.1f
231
232 #define AUTOSCROLL_INTERVAL             0.1f
233
234 #define DRAG_LABEL_BORDER_X             4.0f
235 //Keep border_y in synch with DragController::LinkDragBorderInset
236 #define DRAG_LABEL_BORDER_Y             2.0f
237 #define DRAG_LABEL_RADIUS               5.0f
238 #define DRAG_LABEL_BORDER_Y_OFFSET              2.0f
239
240 #define MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP        120.0f
241 #define MAX_DRAG_LABEL_WIDTH                    320.0f
242
243 #define DRAG_LINK_LABEL_FONT_SIZE   11.0f
244 #define DRAG_LINK_URL_FONT_SIZE   10.0f
245
246 // Any non-zero value will do, but using something recognizable might help us debug some day.
247 #define TRACKING_RECT_TAG 0xBADFACE
248
249 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
250 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
251
252 #define STANDARD_WEIGHT 5
253 #define MIN_BOLD_WEIGHT 9
254 #define STANDARD_BOLD_WEIGHT 10
255
256 // Fake URL scheme.
257 #define WebDataProtocolScheme @"webkit-fake-url"
258
259 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
260 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
261 @interface WebCoreScrollView : NSScrollView
262 @end
263
264 @implementation WebCoreScrollView
265 @end
266
267 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
268 static BOOL forceNSViewHitTest;
269
270 // if YES, do the "top WebHTMLView" hit test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721])
271 static BOOL forceWebHTMLViewHitTest;
272
273 static WebHTMLView *lastHitView;
274
275 // We need this to be able to safely reference the CachedImage for the promised drag data
276 static CachedResourceClient* promisedDataClient()
277 {
278     static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
279     return staticCachedResourceClient;
280 }
281
282 @interface WebHTMLView (WebTextSizing) <_WebDocumentTextSizing>
283 @end
284
285 @interface WebHTMLView (WebHTMLViewFileInternal)
286 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
287 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
288 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
289 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
290 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
291 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
292 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
293 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
294 - (float)_calculatePrintHeight;
295 - (void)_updateTextSizeMultiplier;
296 - (DOMRange *)_selectedRange;
297 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
298 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard;
299 - (NSView *)_hitViewForEvent:(NSEvent *)event;
300 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
301 - (DOMRange *)_documentRange;
302 - (WebFrameBridge *)_bridge;
303 - (void)_setMouseDownEvent:(NSEvent *)event;
304 - (WebHTMLView *)_topHTMLView;
305 - (BOOL)_isTopHTMLView;
306 - (void)_web_setPrintingModeRecursive;
307 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
308 - (void)_web_clearPrintingModeRecursive;
309 @end
310
311 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
312 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize;
313 @end
314
315 @class NSInputContext;
316 @interface NSResponder (IMSecretsIKnowAbout)
317 - (NSInputContext *)inputContext;
318 @end
319
320 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
321 - (void)_updateSelectionForInputManager;
322 @end
323
324 @interface WebHTMLView (WebEditingStyleSupport)
325 - (DOMCSSStyleDeclaration *)_emptyStyle;
326 - (NSString *)_colorAsString:(NSColor *)color;
327 @end
328
329 @interface NSView (WebHTMLViewFileInternal)
330 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
331 @end
332
333 @interface NSMutableDictionary (WebHTMLViewFileInternal)
334 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
335 @end
336
337 // Handles the complete: text command
338 @interface WebTextCompleteController : NSObject {
339 @private
340     WebHTMLView *_view;
341     NSWindow *_popupWindow;
342     NSTableView *_tableView;
343     NSArray *_completions;
344     NSString *_originalString;
345     int prefixLength;
346 }
347 - (id)initWithHTMLView:(WebHTMLView *)view;
348 - (void)doCompletion;
349 - (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft;
350 - (BOOL)popupWindowIsOpen;
351 - (BOOL)filterKeyDown:(NSEvent *)event;
352 - (void)_reflectSelection;
353 @end
354
355 struct WebHTMLViewInterpretKeyEventsParameters {
356     KeyboardEvent* event;
357     BOOL eventWasHandled;
358     BOOL shouldSaveCommand;
359     // The Input Method may consume an event and not tell us, in
360     // which case we should not bubble the event up the DOM
361     BOOL consumedByIM;
362 };
363
364 static NSCellStateValue kit(TriState state)
365 {
366     switch (state) {
367         case FalseTriState:
368             return NSOffState;
369         case TrueTriState:
370             return NSOnState;
371         case MixedTriState:
372             return NSMixedState;
373     }
374     ASSERT_NOT_REACHED();
375     return NSOffState;
376 }
377
378 @implementation WebHTMLViewPrivate
379
380 + (void)initialize
381 {
382 #ifndef BUILDING_ON_TIGER
383     WebCoreObjCFinalizeOnMainThread(self);
384 #endif
385
386     if (!oldSetCursorIMP) {
387 #ifdef BUILDING_ON_TIGER
388         Method setCursorMethod = class_getInstanceMethod([NSCursor class], @selector(set));
389 #else
390         Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
391 #endif
392         ASSERT(setCursorMethod);
393
394         oldSetCursorIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
395         ASSERT(oldSetCursorIMP);
396     }
397     
398 #ifdef BUILDING_ON_TIGER
399     if (!oldResetCursorRectsIMP) {
400         Method resetCursorRectsMethod = class_getInstanceMethod([NSWindow class], @selector(resetCursorRects));
401         ASSERT(resetCursorRectsMethod);
402         oldResetCursorRectsIMP = method_setImplementation(resetCursorRectsMethod, (IMP)resetCursorRects);
403         ASSERT(oldResetCursorRectsIMP);
404     }
405 #endif
406
407 }
408
409 - (void)dealloc
410 {
411     ASSERT(!autoscrollTimer);
412     ASSERT(!autoscrollTriggerEvent);
413     ASSERT(!updateActiveStateTimer);
414     ASSERT(!updateMouseoverTimer);
415     
416     [mouseDownEvent release];
417     [keyDownEvent release];
418     [pluginController release];
419     [toolTip release];
420     [compController release];
421     [dataSource release];
422     [highlighters release];
423     if (promisedDragTIFFDataSource)
424         promisedDragTIFFDataSource->deref(promisedDataClient());
425
426     [super dealloc];
427 }
428
429 - (void)finalize
430 {
431     ASSERT_MAIN_THREAD();
432
433     if (promisedDragTIFFDataSource)
434         promisedDragTIFFDataSource->deref(promisedDataClient());
435
436     [super finalize];
437 }
438
439 - (void)clear
440 {
441     [mouseDownEvent release];
442     [keyDownEvent release];
443     [pluginController release];
444     [toolTip release];
445     [compController release];
446     [dataSource release];
447     [highlighters release];
448     if (promisedDragTIFFDataSource)
449         promisedDragTIFFDataSource->deref(promisedDataClient());
450
451     mouseDownEvent = nil;
452     keyDownEvent = nil;
453     pluginController = nil;
454     toolTip = nil;
455     compController = nil;
456     dataSource = nil;
457     highlighters = nil;
458     promisedDragTIFFDataSource = 0;
459 }
460
461 @end
462
463 @implementation WebHTMLView (WebHTMLViewFileInternal)
464
465 - (DOMRange *)_documentRange
466 {
467     return [[[self _frame] DOMDocument] _documentRange];
468 }
469
470 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
471 {
472     NSEnumerator *enumerator = [paths objectEnumerator];
473     NSString *path;
474     
475     while ((path = [enumerator nextObject]) != nil) {
476         NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
477         if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
478             return YES;
479     }
480     
481     return NO;
482 }
483
484 - (WebDataSource *)_dataSource
485 {
486     return _private->dataSource;
487 }
488
489 - (WebFrameBridge *)_bridge
490 {
491     return [_private->dataSource _bridge];
492 }
493
494 - (WebView *)_webView
495 {
496     return [_private->dataSource _webView];
497 }
498
499 - (WebFrameView *)_frameView
500 {
501     return [[_private->dataSource webFrame] frameView];
502 }
503
504 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
505 {
506     DOMDocumentFragment *fragment;
507     NSEnumerator *enumerator = [paths objectEnumerator];
508     NSMutableArray *domNodes = [[NSMutableArray alloc] init];
509     NSString *path;
510     
511     while ((path = [enumerator nextObject]) != nil) {
512         // Non-image file types; _web_userVisibleString is appropriate here because this will
513         // be pasted as visible text.
514         NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
515         [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
516     }
517     
518     fragment = [[self _bridge] documentFragmentWithNodesAsParagraphs:domNodes]; 
519     
520     [domNodes release];
521     
522     return [fragment firstChild] != nil ? fragment : nil;
523 }
524
525 + (NSArray *)_excludedElementsForAttributedStringConversion
526 {
527     static NSArray *elements = nil;
528     if (elements == nil) {
529         elements = [[NSArray alloc] initWithObjects:
530             // Omit style since we want style to be inline so the fragment can be easily inserted.
531             @"style",
532             // Omit xml so the result is not XHTML.
533             @"xml", 
534             // Omit tags that will get stripped when converted to a fragment anyway.
535             @"doctype", @"html", @"head", @"body",
536             // Omit deprecated tags.
537             @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
538             // Omit object so no file attachments are part of the fragment.
539             @"object", nil];
540         CFRetain(elements);
541     }
542     return elements;
543 }
544
545 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
546 {
547     CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
548     NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
549     CFRelease(UUIDRef);
550     NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
551     CFRelease(UUIDString);
552
553     return URL;
554 }
555
556 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
557                                                inContext:(DOMRange *)context
558                                           allowPlainText:(BOOL)allowPlainText
559 {
560     NSArray *types = [pasteboard types];
561     DOMDocumentFragment *fragment = nil;
562
563     if ([types containsObject:WebArchivePboardType] &&
564         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
565                                                   forType:WebArchivePboardType
566                                                 inContext:context
567                                              subresources:0]))
568         return fragment;
569                                            
570     if ([types containsObject:NSFilenamesPboardType] &&
571         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
572                                                   forType:NSFilenamesPboardType
573                                                 inContext:context
574                                              subresources:0]))
575         return fragment;
576     
577     if ([types containsObject:NSHTMLPboardType] &&
578         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
579                                                   forType:NSHTMLPboardType
580                                                 inContext:context
581                                              subresources:0]))
582         return fragment;
583     
584     if ([types containsObject:NSRTFPboardType] &&
585         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
586                                                   forType:NSRTFPboardType
587                                                 inContext:context
588                                              subresources:0]))
589         return fragment;
590
591     if ([types containsObject:NSRTFDPboardType] &&
592         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
593                                                   forType:NSRTFDPboardType
594                                                 inContext:context
595                                              subresources:0]))
596         return fragment;
597
598     if ([types containsObject:NSTIFFPboardType] &&
599         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
600                                                   forType:NSTIFFPboardType
601                                                 inContext:context
602                                              subresources:0]))
603         return fragment;
604
605     if ([types containsObject:NSPICTPboardType] &&
606         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
607                                                   forType:NSPICTPboardType
608                                                 inContext:context
609                                              subresources:0]))
610         return fragment;
611     
612     if ([types containsObject:NSURLPboardType] &&
613         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
614                                                   forType:NSURLPboardType
615                                                 inContext:context
616                                              subresources:0]))
617         return fragment;
618         
619     if (allowPlainText && [types containsObject:NSStringPboardType] &&
620         (fragment = [self _documentFragmentFromPasteboard:pasteboard
621                                                   forType:NSStringPboardType
622                                                 inContext:context
623                                              subresources:0])) {
624         return fragment;
625     }
626     
627     return nil;
628 }
629
630 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
631 {
632     NSArray *types = [pasteboard types];
633     
634     if ([types containsObject:NSStringPboardType])
635         return [pasteboard stringForType:NSStringPboardType];
636     
637     NSAttributedString *attributedString = nil;
638     NSString *string;
639
640     if ([types containsObject:NSRTFDPboardType])
641         attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
642     if (attributedString == nil && [types containsObject:NSRTFPboardType])
643         attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
644     if (attributedString != nil) {
645         string = [[attributedString string] copy];
646         [attributedString release];
647         return [string autorelease];
648     }
649     
650     if ([types containsObject:NSFilenamesPboardType]) {
651         string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
652         if (string != nil)
653             return string;
654     }
655     
656     NSURL *URL;
657     
658     if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
659         string = [URL _web_userVisibleString];
660         if ([string length] > 0)
661             return string;
662     }
663     
664     return nil;
665 }
666
667 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
668 {
669     DOMRange *range = [self _selectedRange];
670     DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard
671         inContext:range allowPlainText:allowPlainText];
672     WebFrameBridge *bridge = [self _bridge];
673     if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:[self _selectedRange] givenAction:WebViewInsertActionPasted]) {
674         [bridge replaceSelectionWithFragment:fragment selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard] matchStyle:NO];
675     }
676 }
677
678 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard
679 {
680     NSString *text = [self _plainTextFromPasteboard:pasteboard];
681     if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted])
682         [[self _bridge] replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]];
683 }
684
685 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
686 {
687     WebView *webView = [self _webView];
688     DOMNode *child = [fragment firstChild];
689     if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
690         return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
691     return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
692 }
693
694 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
695 {
696     WebView *webView = [self _webView];
697     return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
698 }
699
700 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
701 {
702     return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
703 }
704
705 // Calculate the vertical size of the view that fits on a single page
706 - (float)_calculatePrintHeight
707 {
708     // Obtain the print info object for the current operation
709     NSPrintInfo *pi = [[NSPrintOperation currentOperation] printInfo];
710     
711     // Calculate the page height in points
712     NSSize paperSize = [pi paperSize];
713     return paperSize.height - [pi topMargin] - [pi bottomMargin];
714 }
715
716 - (void)_updateTextSizeMultiplier
717 {
718     [[self _bridge] setTextSizeMultiplier:[[self _webView] textSizeMultiplier]];    
719 }
720
721 - (DOMRange *)_selectedRange
722 {
723     Frame* coreFrame = core([self _frame]);
724     return coreFrame ? kit(coreFrame->selectionController()->toRange().get()) : nil;
725 }
726
727 - (BOOL)_shouldDeleteRange:(DOMRange *)range
728 {
729     Frame* coreFrame = core([self _frame]);
730     return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
731 }
732
733 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
734 {
735     return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
736 }
737
738 - (NSView *)_hitViewForEvent:(NSEvent *)event
739 {
740     // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.  
741     // Callers of this method, however, want to query the deepest view instead.
742     forceNSViewHitTest = YES;
743     NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]];
744     forceNSViewHitTest = NO;    
745     return hitView;
746 }
747
748 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
749 {
750     // Put HTML on the pasteboard.
751     if ([types containsObject:WebArchivePboardType]) {
752         WebArchive *archive = [WebArchiver archiveSelectionInFrame:[self _frame]];
753         [pasteboard setData:[archive data] forType:WebArchivePboardType];
754     }
755     
756     // Put the attributed string on the pasteboard (RTF/RTFD format).
757     if ([types containsObject:NSRTFDPboardType]) {
758         if (attributedString == nil) {
759             attributedString = [self selectedAttributedString];
760         }        
761         NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
762         [pasteboard setData:RTFDData forType:NSRTFDPboardType];
763     }        
764     if ([types containsObject:NSRTFPboardType]) {
765         if (attributedString == nil) {
766             attributedString = [self selectedAttributedString];
767         }
768         if ([attributedString containsAttachments]) {
769             attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters];
770         }
771         NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
772         [pasteboard setData:RTFData forType:NSRTFPboardType];
773     }
774     
775     // Put plain string on the pasteboard.
776     if ([types containsObject:NSStringPboardType]) {
777         // Map &nbsp; to a plain old space because this is better for source code, other browsers do it,
778         // and because HTML forces you to do this any time you want two spaces in a row.
779         NSMutableString *s = [[self selectedString] mutableCopy];
780         const unichar NonBreakingSpaceCharacter = 0xA0;
781         NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
782         [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
783         [pasteboard setString:s forType:NSStringPboardType];
784         [s release];
785     }
786     
787     if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
788         [pasteboard setData:nil forType:WebSmartPastePboardType];
789     }
790 }
791
792 - (void)_setMouseDownEvent:(NSEvent *)event
793 {
794     ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
795
796     if (event == _private->mouseDownEvent)
797         return;
798
799     [event retain];
800     [_private->mouseDownEvent release];
801     _private->mouseDownEvent = event;
802 }
803
804 - (void)_cancelUpdateActiveStateTimer
805 {
806     if (_private->updateActiveStateTimer) {
807         CFRunLoopTimerInvalidate(_private->updateActiveStateTimer);
808         CFRelease(_private->updateActiveStateTimer);
809         _private->updateActiveStateTimer = NULL;
810     }
811 }
812
813 - (void)_cancelUpdateMouseoverTimer
814 {
815     if (_private->updateMouseoverTimer) {
816         CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
817         CFRelease(_private->updateMouseoverTimer);
818         _private->updateMouseoverTimer = NULL;
819     }
820 }
821
822 - (WebHTMLView *)_topHTMLView
823 {
824     // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
825     WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
826     ASSERT(view);
827     ASSERT([view isKindOfClass:[WebHTMLView class]]);
828     return view;
829 }
830
831 - (BOOL)_isTopHTMLView
832 {
833     // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
834     return self == [self _topHTMLView];
835 }
836
837 - (void)_web_setPrintingModeRecursive
838 {
839     [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
840
841 #ifndef NDEBUG
842     _private->enumeratingSubviews = YES;
843 #endif
844
845     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
846
847     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
848
849     unsigned count = [descendantWebHTMLViews count];
850     for (unsigned i = 0; i < count; ++i)
851         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
852
853     [descendantWebHTMLViews release];
854
855 #ifndef NDEBUG
856     _private->enumeratingSubviews = NO;
857 #endif
858 }
859
860 - (void)_web_clearPrintingModeRecursive
861 {
862     [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
863
864 #ifndef NDEBUG
865     _private->enumeratingSubviews = YES;
866 #endif
867
868     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
869
870     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
871
872     unsigned count = [descendantWebHTMLViews count];
873     for (unsigned i = 0; i < count; ++i)
874         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
875
876     [descendantWebHTMLViews release];
877
878 #ifndef NDEBUG
879     _private->enumeratingSubviews = NO;
880 #endif
881 }
882
883 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize
884 {
885     [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
886
887 #ifndef NDEBUG
888     _private->enumeratingSubviews = YES;
889 #endif
890
891     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
892
893     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
894
895     unsigned count = [descendantWebHTMLViews count];
896     for (unsigned i = 0; i < count; ++i)
897         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
898
899     [descendantWebHTMLViews release];
900
901 #ifndef NDEBUG
902     _private->enumeratingSubviews = NO;
903 #endif
904 }
905
906 @end
907
908 @implementation WebHTMLView (WebPrivate)
909
910 + (NSArray *)supportedMIMETypes
911 {
912     return [WebHTMLRepresentation supportedMIMETypes];
913 }
914
915 + (NSArray *)supportedImageMIMETypes
916 {
917     return [WebHTMLRepresentation supportedImageMIMETypes];
918 }
919
920 + (NSArray *)supportedNonImageMIMETypes
921 {
922     return [WebHTMLRepresentation supportedNonImageMIMETypes];
923 }
924
925 + (NSArray *)unsupportedTextMIMETypes
926 {
927     return [NSArray arrayWithObjects:
928         @"text/calendar",       // iCal
929         @"text/x-calendar",
930         @"text/x-vcalendar",
931         @"text/vcalendar",
932         @"text/vcard",          // vCard
933         @"text/x-vcard",
934         @"text/directory",
935         @"text/ldif",           // Netscape Address Book
936         @"text/qif",            // Quicken
937         @"text/x-qif",
938         @"text/x-csv",          // CSV (for Address Book and Microsoft Outlook)
939         @"text/x-vcf",          // vCard type used in Sun affinity app
940         @"text/rtf",            // Rich Text Format
941         nil];
942 }
943
944 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
945 {
946     // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged
947     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
948         location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
949         modifierFlags:[flagsChangedEvent modifierFlags]
950         timestamp:[flagsChangedEvent timestamp]
951         windowNumber:[flagsChangedEvent windowNumber]
952         context:[flagsChangedEvent context]
953         eventNumber:0 clickCount:0 pressure:0];
954
955     // Pretend it's a mouse move.
956     [[NSNotificationCenter defaultCenter]
957         postNotificationName:WKMouseMovedNotification() object:self
958         userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
959 }
960
961 - (void)_updateMouseoverWithFakeEvent
962 {
963     [self _cancelUpdateMouseoverTimer];
964     
965     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
966         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
967         modifierFlags:[[NSApp currentEvent] modifierFlags]
968         timestamp:[NSDate timeIntervalSinceReferenceDate]
969         windowNumber:[[self window] windowNumber]
970         context:[[NSApp currentEvent] context]
971         eventNumber:0 clickCount:0 pressure:0];
972     
973     [self _updateMouseoverWithEvent:fakeEvent];
974 }
975
976 static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
977 {
978     WebHTMLView *view = (WebHTMLView *)info;
979     
980     [view _updateMouseoverWithFakeEvent];
981 }
982
983 - (void)_frameOrBoundsChanged
984 {
985     if (!NSEqualSizes(_private->lastLayoutSize, [(NSClipView *)[self superview] documentVisibleRect].size)) {
986         [self setNeedsLayout:YES];
987         [self setNeedsDisplay:YES];
988         [_private->compController endRevertingChange:NO moveLeft:NO];
989     }
990
991     NSPoint origin = [[self superview] bounds].origin;
992     if (!NSEqualPoints(_private->lastScrollPosition, origin)) {
993         [[self _bridge] sendScrollEvent];
994         [_private->compController endRevertingChange:NO moveLeft:NO];
995         
996         WebView *webView = [self _webView];
997         [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
998     }
999     _private->lastScrollPosition = origin;
1000
1001     if ([self window] && !_private->closed && !_private->updateMouseoverTimer) {
1002         CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
1003         
1004         // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading
1005         // and scrolling rapidly back to back.
1006         _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0,
1007                                                               _updateMouseoverTimerCallback, &context);
1008         CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode);
1009     }
1010 }
1011
1012 - (void)_setAsideSubviews
1013 {
1014     ASSERT(!_private->subviewsSetAside);
1015     ASSERT(_private->savedSubviews == nil);
1016     _private->savedSubviews = _subviews;
1017     _subviews = nil;
1018     _private->subviewsSetAside = YES;
1019  }
1020  
1021  - (void)_restoreSubviews
1022  {
1023     ASSERT(_private->subviewsSetAside);
1024     ASSERT(_subviews == nil);
1025     _subviews = _private->savedSubviews;
1026     _private->savedSubviews = nil;
1027     _private->subviewsSetAside = NO;
1028 }
1029
1030 #ifndef NDEBUG
1031
1032 - (void)didAddSubview:(NSView *)subview
1033 {
1034     if (_private->enumeratingSubviews)
1035         LOG(View, "A view of class %s was added during subview enumeration for layout or printing mode change. This view might paint without first receiving layout.", object_getClassName([subview class]));
1036 }
1037
1038 - (void)willRemoveSubview:(NSView *)subview
1039 {
1040     if (_private->enumeratingSubviews)
1041         LOG(View, "A view of class %s was removed during subview enumeration for layout or printing mode change. We will still do layout or the printing mode change even though this view is no longer in the view hierarchy.", object_getClassName([subview class]));
1042 }
1043
1044 #endif
1045
1046 #ifdef BUILDING_ON_TIGER
1047
1048 // This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
1049 // That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty
1050 // before doing it. As a compromise, when we're opaque we do the layout only when actually asked to
1051 // draw, but when we're transparent we do the layout at this stage so views behind us know that they
1052 // need to be redrawn (in case the layout causes some things to get dirtied).
1053 - (void)_propagateDirtyRectsToOpaqueAncestors
1054 {
1055     if (![[self _webView] drawsBackground])
1056         [self _web_layoutIfNeededRecursive];
1057     [super _propagateDirtyRectsToOpaqueAncestors];
1058 }
1059
1060 #else
1061
1062 - (void)viewWillDraw
1063 {
1064     // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1065     // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1066     // once the FIXME in _isTopHTMLView is fixed.
1067     if (_private->dataSource && [self _isTopHTMLView])
1068         [self _web_layoutIfNeededRecursive];
1069     [super viewWillDraw];
1070 }
1071
1072 #endif
1073
1074 // Don't let AppKit even draw subviews. We take care of that.
1075 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1076 {
1077     // This helps when we print as part of a larger print process.
1078     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1079     BOOL wasInPrintingMode = _private->printing;
1080     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1081     if (wasInPrintingMode != isPrinting) {
1082         if (isPrinting)
1083             [self _web_setPrintingModeRecursive];
1084         else
1085             [self _web_clearPrintingModeRecursive];
1086     }
1087
1088 #ifdef BUILDING_ON_TIGER
1089
1090     // Because Tiger does not have viewWillDraw we need to do layout here.
1091     [self _web_layoutIfNeededRecursive];
1092     [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)];
1093
1094 #endif
1095
1096     [self _setAsideSubviews];
1097     [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1098     [self _restoreSubviews];
1099
1100     if (wasInPrintingMode != isPrinting) {
1101         if (wasInPrintingMode)
1102             [self _web_setPrintingModeRecursive];
1103         else
1104             [self _web_clearPrintingModeRecursive];
1105     }
1106 }
1107
1108 // Don't let AppKit even draw subviews. We take care of that.
1109 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1110 {
1111     BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1112
1113     BOOL wasInPrintingMode = _private->printing;
1114     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1115
1116     if (needToSetAsideSubviews) {
1117         // This helps when we print as part of a larger print process.
1118         // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1119         if (wasInPrintingMode != isPrinting) {
1120             if (isPrinting)
1121                 [self _web_setPrintingModeRecursive];
1122             else
1123                 [self _web_clearPrintingModeRecursive];
1124         }
1125
1126 #ifdef BUILDING_ON_TIGER
1127
1128         // Because Tiger does not have viewWillDraw we need to do layout here.
1129         NSRect boundsBeforeLayout = [self bounds];
1130         if (!NSIsEmptyRect(visRect))
1131             [self _web_layoutIfNeededRecursive];
1132
1133         // If layout changes the view's bounds, then we need to recompute the visRect.
1134         // That's because the visRect passed to us was based on the bounds at the time
1135         // we were called. This method is only displayed to draw "all", so it's safe
1136         // to just call visibleRect to compute the entire rectangle.
1137         if (!NSEqualRects(boundsBeforeLayout, [self bounds]))
1138             visRect = [self visibleRect];
1139
1140 #endif
1141
1142         [self _setAsideSubviews];
1143     }
1144
1145     [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1146
1147     if (needToSetAsideSubviews) {
1148         if (wasInPrintingMode != isPrinting) {
1149             if (wasInPrintingMode)
1150                 [self _web_setPrintingModeRecursive];
1151             else
1152                 [self _web_clearPrintingModeRecursive];
1153         }
1154
1155         [self _restoreSubviews];
1156     }
1157 }
1158
1159 - (BOOL)_insideAnotherHTMLView
1160 {
1161     return self != [self _topHTMLView];
1162 }
1163
1164 - (NSView *)hitTest:(NSPoint)point
1165 {
1166     // WebHTMLView objects handle all events for objects inside them.
1167     // To get those events, we prevent hit testing from AppKit.
1168
1169     // But there are three exceptions to this:
1170     //   1) For right mouse clicks and control clicks we don't yet have an implementation
1171     //      that works for nested views, so we let the hit testing go through the
1172     //      standard NSView code path (needs to be fixed, see bug 4361618).
1173     //   2) Java depends on doing a hit test inside it's mouse moved handling,
1174     //      so we let the hit testing go through the standard NSView code path
1175     //      when the current event is a mouse move (except when we are calling
1176     //      from _updateMouseoverWithEvent, so we have to use a global,
1177     //      forceWebHTMLViewHitTest, for that)
1178     //   3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1179     //      both need to figure out which view to check with inside the WebHTMLView.
1180     //      They use a global to change the behavior of hitTest: so they can get the
1181     //      right view. The global is forceNSViewHitTest and the method they use to
1182     //      do the hit testing is _hitViewForEvent:. (But this does not work correctly
1183     //      when there is HTML overlapping the view, see bug 4361626)
1184     //   4) NSAccessibilityHitTest relies on this for checking the cursor position.
1185     //      Our check for that is whether the event is NSFlagsChanged.  This works
1186     //      for VoiceOver's cntl-opt-f5 command (move focus to item under cursor)
1187     //      and Dictionary's cmd-cntl-D (open dictionary popup for item under cursor).
1188     //      This is of course a hack.
1189
1190     BOOL captureHitsOnSubviews;
1191     if (forceNSViewHitTest)
1192         captureHitsOnSubviews = NO;
1193     else if (forceWebHTMLViewHitTest)
1194         captureHitsOnSubviews = YES;
1195     else {
1196         NSEvent *event = [[self window] currentEvent];
1197         captureHitsOnSubviews = !([event type] == NSMouseMoved
1198             || [event type] == NSRightMouseDown
1199             || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1200             || [event type] == NSFlagsChanged);
1201     }
1202
1203     if (!captureHitsOnSubviews)
1204         return [super hitTest:point];
1205     if ([[self superview] mouse:point inRect:[self frame]])
1206         return self;
1207     return nil;
1208 }
1209
1210 - (void)_clearLastHitViewIfSelf
1211 {
1212     if (lastHitView == self)
1213         lastHitView = nil;
1214 }
1215
1216 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1217 {
1218     ASSERT(_private->trackingRectOwner == nil);
1219     _private->trackingRectOwner = owner;
1220     _private->trackingRectUserData = data;
1221     return TRACKING_RECT_TAG;
1222 }
1223
1224 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1225 {
1226     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1227     ASSERT(_private->trackingRectOwner == nil);
1228     _private->trackingRectOwner = owner;
1229     _private->trackingRectUserData = data;
1230     return TRACKING_RECT_TAG;
1231 }
1232
1233 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1234 {
1235     ASSERT(count == 1);
1236     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1237     ASSERT(_private->trackingRectOwner == nil);
1238     _private->trackingRectOwner = owner;
1239     _private->trackingRectUserData = userDataList[0];
1240     trackingNums[0] = TRACKING_RECT_TAG;
1241 }
1242
1243 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1244 {
1245     if (tag == 0)
1246         return;
1247     
1248     if (_private && (tag == TRACKING_RECT_TAG)) {
1249         _private->trackingRectOwner = nil;
1250         return;
1251     }
1252     
1253     if (_private && (tag == _private->lastToolTipTag)) {
1254         [super removeTrackingRect:tag];
1255         _private->lastToolTipTag = 0;
1256         return;
1257     }
1258     
1259     // If any other tracking rect is being removed, we don't know how it was created
1260     // and it's possible there's a leak involved (see 3500217)
1261     ASSERT_NOT_REACHED();
1262 }
1263
1264 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1265 {
1266     int i;
1267     for (i = 0; i < count; ++i) {
1268         int tag = tags[i];
1269         if (tag == 0)
1270             continue;
1271         ASSERT(tag == TRACKING_RECT_TAG);
1272         if (_private != nil) {
1273             _private->trackingRectOwner = nil;
1274         }
1275     }
1276 }
1277
1278 - (void)_sendToolTipMouseExited
1279 {
1280     // Nothing matters except window, trackingNumber, and userData.
1281     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1282         location:NSMakePoint(0, 0)
1283         modifierFlags:0
1284         timestamp:0
1285         windowNumber:[[self window] windowNumber]
1286         context:NULL
1287         eventNumber:0
1288         trackingNumber:TRACKING_RECT_TAG
1289         userData:_private->trackingRectUserData];
1290     [_private->trackingRectOwner mouseExited:fakeEvent];
1291 }
1292
1293 - (void)_sendToolTipMouseEntered
1294 {
1295     // Nothing matters except window, trackingNumber, and userData.
1296     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1297         location:NSMakePoint(0, 0)
1298         modifierFlags:0
1299         timestamp:0
1300         windowNumber:[[self window] windowNumber]
1301         context:NULL
1302         eventNumber:0
1303         trackingNumber:TRACKING_RECT_TAG
1304         userData:_private->trackingRectUserData];
1305     [_private->trackingRectOwner mouseEntered:fakeEvent];
1306 }
1307
1308 - (void)_setToolTip:(NSString *)string
1309 {
1310     NSString *toolTip = [string length] == 0 ? nil : string;
1311     NSString *oldToolTip = _private->toolTip;
1312     if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1313         return;
1314     }
1315     if (oldToolTip) {
1316         [self _sendToolTipMouseExited];
1317         [oldToolTip release];
1318     }
1319     _private->toolTip = [toolTip copy];
1320     if (toolTip) {
1321         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1322         [self removeAllToolTips];
1323         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1324         _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1325         [self _sendToolTipMouseEntered];
1326     }
1327 }
1328
1329 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1330 {
1331     return [[_private->toolTip copy] autorelease];
1332 }
1333
1334 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1335 {
1336     if (_private->closed)
1337         return;
1338
1339     NSView *contentView = [[event window] contentView];
1340     NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1341     
1342     forceWebHTMLViewHitTest = YES;
1343     NSView *hitView = [contentView hitTest:locationForHitTest];
1344     forceWebHTMLViewHitTest = NO;
1345     
1346     WebHTMLView *view = nil;
1347     if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
1348         view = (WebHTMLView *)hitView;    
1349
1350     if (view)
1351         [view retain];
1352
1353     if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1354         // If we are moving out of a view (or frame), let's pretend the mouse moved
1355         // all the way out of that view. But we have to account for scrolling, because
1356         // khtml doesn't understand our clipping.
1357         NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1358         float yScroll = visibleRect.origin.y;
1359         float xScroll = visibleRect.origin.x;
1360
1361         event = [NSEvent mouseEventWithType:NSMouseMoved
1362                          location:NSMakePoint(-1 - xScroll, -1 - yScroll )
1363                          modifierFlags:[[NSApp currentEvent] modifierFlags]
1364                          timestamp:[NSDate timeIntervalSinceReferenceDate]
1365                          windowNumber:[[view window] windowNumber]
1366                          context:[[NSApp currentEvent] context]
1367                          eventNumber:0 clickCount:0 pressure:0];
1368         if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1369             lastHitCoreFrame->eventHandler()->mouseMoved(event);
1370     }
1371
1372     lastHitView = view;
1373
1374     if (view) {
1375         if (Frame* coreFrame = core([view _frame]))
1376             coreFrame->eventHandler()->mouseMoved(event);
1377
1378         [view release];
1379     }
1380 }
1381
1382 // keep in sync with WebPasteboardHelper::insertablePasteboardTypes
1383 + (NSArray *)_insertablePasteboardTypes
1384 {
1385     static NSArray *types = nil;
1386     if (!types) {
1387         types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType,
1388             NSFilenamesPboardType, NSTIFFPboardType, NSPICTPboardType, NSURLPboardType, 
1389             NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, nil];
1390         CFRetain(types);
1391     }
1392     return types;
1393 }
1394
1395 + (NSArray *)_selectionPasteboardTypes
1396 {
1397     // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1398     return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1399 }
1400
1401 - (NSImage *)_dragImageForURL:(NSString*)urlString withLabel:(NSString*)label
1402 {
1403     BOOL drawURLString = YES;
1404     BOOL clipURLString = NO, clipLabelString = NO;
1405     
1406     if (!label) {
1407         drawURLString = NO;
1408         label = urlString;
1409     }
1410     
1411     NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DRAG_LINK_LABEL_FONT_SIZE]
1412                                                            toHaveTrait:NSBoldFontMask];
1413     NSFont *urlFont = [NSFont systemFontOfSize: DRAG_LINK_URL_FONT_SIZE];
1414     NSSize labelSize;
1415     labelSize.width = [label _web_widthWithFont: labelFont];
1416     labelSize.height = [labelFont ascender] - [labelFont descender];
1417     if (labelSize.width > MAX_DRAG_LABEL_WIDTH){
1418         labelSize.width = MAX_DRAG_LABEL_WIDTH;
1419         clipLabelString = YES;
1420     }
1421     
1422     NSSize imageSize, urlStringSize;
1423     imageSize.width = labelSize.width + DRAG_LABEL_BORDER_X * 2.0f;
1424     imageSize.height = labelSize.height + DRAG_LABEL_BORDER_Y * 2.0f;
1425     if (drawURLString) {
1426         urlStringSize.width = [urlString _web_widthWithFont: urlFont];
1427         urlStringSize.height = [urlFont ascender] - [urlFont descender];
1428         imageSize.height += urlStringSize.height;
1429         if (urlStringSize.width > MAX_DRAG_LABEL_WIDTH) {
1430             imageSize.width = MAX(MAX_DRAG_LABEL_WIDTH + DRAG_LABEL_BORDER_X * 2.0f, MIN_DRAG_LABEL_WIDTH_BEFORE_CLIP);
1431             clipURLString = YES;
1432         } else {
1433             imageSize.width = MAX(labelSize.width + DRAG_LABEL_BORDER_X * 2.0f, urlStringSize.width + DRAG_LABEL_BORDER_X * 2.0f);
1434         }
1435     }
1436     NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease];
1437     [dragImage lockFocus];
1438     
1439     [[NSColor colorWithCalibratedRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set];
1440     
1441     // Drag a rectangle with rounded corners/
1442     NSBezierPath *path = [NSBezierPath bezierPath];
1443     [path appendBezierPathWithOvalInRect: NSMakeRect(0.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1444     [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1445     [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)];
1446     [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DRAG_LABEL_RADIUS * 2.0f, 0.0f, DRAG_LABEL_RADIUS * 2.0f, DRAG_LABEL_RADIUS * 2.0f)];
1447     
1448     [path appendBezierPathWithRect: NSMakeRect(DRAG_LABEL_RADIUS, 0.0f, imageSize.width - DRAG_LABEL_RADIUS * 2.0f, imageSize.height)];
1449     [path appendBezierPathWithRect: NSMakeRect(0.0f, DRAG_LABEL_RADIUS, DRAG_LABEL_RADIUS + 10.0f, imageSize.height - 2.0f * DRAG_LABEL_RADIUS)];
1450     [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)];
1451     [path fill];
1452     
1453     NSColor *topColor = [NSColor colorWithCalibratedWhite:0.0f alpha:0.75f];
1454     NSColor *bottomColor = [NSColor colorWithCalibratedWhite:1.0f alpha:0.5f];
1455     if (drawURLString) {
1456         if (clipURLString)
1457             urlString = [WebStringTruncator centerTruncateString: urlString toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:urlFont];
1458         
1459         [urlString _web_drawDoubledAtPoint:NSMakePoint(DRAG_LABEL_BORDER_X, DRAG_LABEL_BORDER_Y - [urlFont descender]) 
1460                               withTopColor:topColor bottomColor:bottomColor font:urlFont];
1461     }
1462     
1463     if (clipLabelString)
1464         label = [WebStringTruncator rightTruncateString: label toWidth:imageSize.width - (DRAG_LABEL_BORDER_X * 2.0f) withFont:labelFont];
1465     [label _web_drawDoubledAtPoint:NSMakePoint (DRAG_LABEL_BORDER_X, imageSize.height - DRAG_LABEL_BORDER_Y_OFFSET - [labelFont pointSize])
1466                       withTopColor:topColor bottomColor:bottomColor font:labelFont];
1467     
1468     [dragImage unlockFocus];
1469     
1470     return dragImage;
1471 }
1472
1473 - (NSImage *)_dragImageForLinkElement:(NSDictionary *)element
1474 {
1475     NSURL *linkURL = [element objectForKey: WebElementLinkURLKey];
1476     
1477     NSString *label = [element objectForKey: WebElementLinkLabelKey];
1478     NSString *urlString = [linkURL _web_userVisibleString];
1479     return [self _dragImageForURL:urlString withLabel:label];
1480 }
1481
1482 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1483 {
1484     [self setPromisedDragTIFFDataSource:0];
1485 }
1486
1487 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1488 {
1489     if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1490         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1491         [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1492         [archive release];
1493     } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1494         if (Image* image = [self promisedDragTIFFDataSource]->image())
1495             [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1496         [self setPromisedDragTIFFDataSource:0];
1497     }
1498 }
1499
1500 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event 
1501
1502     [self autoscroll:event]; 
1503     [self _startAutoscrollTimer:event]; 
1504
1505
1506 - (WebPluginController *)_pluginController
1507 {
1508     return _private->pluginController;
1509 }
1510
1511 - (void)_layoutForPrinting
1512 {
1513     // Set printing mode temporarily so we can adjust the size of the view. This will allow
1514     // AppKit's pagination code to use the correct height for the page content. Leaving printing
1515     // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1516     // turn it off again after adjusting the size.
1517     [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1518     [self _web_clearPrintingModeRecursive];
1519 }
1520
1521 - (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
1522 {
1523     if (_private->autoscrollTimer == nil) {
1524         _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1525             target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1526         _private->autoscrollTriggerEvent = [triggerEvent retain];
1527     }
1528 }
1529
1530 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1531 // We can't remove this yet because it's still in use by Mail.
1532 - (NSRect)_selectionRect
1533 {
1534     return [self selectionRect];
1535 }
1536
1537 - (void)_stopAutoscrollTimer
1538 {
1539     NSTimer *timer = _private->autoscrollTimer;
1540     _private->autoscrollTimer = nil;
1541     [_private->autoscrollTriggerEvent release];
1542     _private->autoscrollTriggerEvent = nil;
1543     [timer invalidate];
1544     [timer release];
1545 }
1546
1547 - (void)_autoscroll
1548 {
1549     // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1550     // a mouse up event.
1551     BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);   
1552     if (!isStillDown){
1553         [self _stopAutoscrollTimer];
1554         return;
1555     }
1556
1557     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1558         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1559         modifierFlags:[[NSApp currentEvent] modifierFlags]
1560         timestamp:[NSDate timeIntervalSinceReferenceDate]
1561         windowNumber:[[self window] windowNumber]
1562         context:[[NSApp currentEvent] context]
1563         eventNumber:0 clickCount:0 pressure:0];
1564     [self mouseDragged:fakeEvent];
1565 }
1566
1567 - (BOOL)_canEdit
1568 {
1569     Frame* coreFrame = core([self _frame]);
1570     return coreFrame && coreFrame->editor()->canEdit();
1571 }
1572
1573 - (BOOL)_canEditRichly
1574 {
1575     Frame* coreFrame = core([self _frame]);
1576     return coreFrame && coreFrame->editor()->canEditRichly();
1577 }
1578
1579 - (BOOL)_canAlterCurrentSelection
1580 {
1581     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1582 }
1583
1584 - (BOOL)_hasSelection
1585 {
1586     Frame* coreFrame = core([self _frame]);
1587     return coreFrame && coreFrame->selectionController()->isRange();
1588 }
1589
1590 - (BOOL)_hasSelectionOrInsertionPoint
1591 {
1592     Frame* coreFrame = core([self _frame]);
1593     return coreFrame && coreFrame->selectionController()->isCaretOrRange();
1594 }
1595
1596 - (BOOL)_hasInsertionPoint
1597 {
1598     Frame* coreFrame = core([self _frame]);
1599     return coreFrame && coreFrame->selectionController()->isCaret();
1600 }
1601
1602 - (BOOL)_isEditable
1603 {
1604     Frame* coreFrame = core([self _frame]);
1605     return coreFrame && coreFrame->selectionController()->isContentEditable();
1606 }
1607
1608 - (BOOL)_transparentBackground
1609 {
1610     return _private->transparentBackground;
1611 }
1612
1613 - (void)_setTransparentBackground:(BOOL)f
1614 {
1615     _private->transparentBackground = f;
1616 }
1617
1618 - (NSImage *)_selectionDraggingImage
1619 {
1620     if ([self _hasSelection]) {
1621         NSImage *dragImage = core([self _frame])->selectionImage();
1622         [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1623         return dragImage;
1624     }
1625     return nil;
1626 }
1627
1628 - (NSRect)_selectionDraggingRect
1629 {
1630     // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1631     return [self selectionRect];
1632 }
1633
1634 - (DOMNode *)_insertOrderedList
1635 {
1636     Frame* coreFrame = core([self _frame]);
1637     return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
1638 }
1639
1640 - (DOMNode *)_insertUnorderedList
1641 {
1642     Frame* coreFrame = core([self _frame]);
1643     return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
1644 }
1645
1646 - (BOOL)_canIncreaseSelectionListLevel
1647 {
1648     Frame* coreFrame = core([self _frame]);
1649     return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
1650 }
1651
1652 - (BOOL)_canDecreaseSelectionListLevel
1653 {
1654     Frame* coreFrame = core([self _frame]);
1655     return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
1656 }
1657
1658 - (DOMNode *)_increaseSelectionListLevel
1659 {
1660     Frame* coreFrame = core([self _frame]);
1661     return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
1662 }
1663
1664 - (DOMNode *)_increaseSelectionListLevelOrdered
1665 {
1666     Frame* coreFrame = core([self _frame]);
1667     return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
1668 }
1669
1670 - (DOMNode *)_increaseSelectionListLevelUnordered
1671 {
1672     Frame* coreFrame = core([self _frame]);
1673     return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
1674 }
1675
1676 - (void)_decreaseSelectionListLevel
1677 {
1678     Frame* coreFrame = core([self _frame]);
1679     if (coreFrame)
1680         coreFrame->editor()->decreaseSelectionListLevel();
1681 }
1682
1683 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1684 {
1685     if (!_private->highlighters)
1686         _private->highlighters = [[NSMutableDictionary alloc] init];
1687     [_private->highlighters setObject:highlighter forKey:type];
1688 }
1689
1690 - (void)_removeHighlighterOfType:(NSString*)type
1691 {
1692     [_private->highlighters removeObjectForKey:type];
1693 }
1694
1695 - (BOOL)_web_firstResponderCausesFocusDisplay
1696 {
1697     return [[self window] firstResponder] == self || [[self window] firstResponder] == [self _frameView];
1698 }
1699
1700 - (void)_updateActiveState
1701 {
1702     [self _cancelUpdateActiveStateTimer];
1703
1704     // This method does the job of updating the view based on the view's firstResponder-ness and
1705     // the window key-ness of the window containing this view. This involves four kinds of 
1706     // drawing updates right now. 
1707     // 
1708     // The four display attributes are as follows:
1709     // 
1710     // 1. The background color used to draw behind selected content (active | inactive color)
1711     // 2. Caret blinking (blinks | does not blink)
1712     // 3. The drawing of a focus ring around links in web pages.
1713     //
1714     // Also, this is responsible for letting the bridge know if the window has gained or lost focus
1715     // so we can send focus and blur events.
1716
1717     Frame* frame = core([self _frame]);
1718     if (!frame)
1719         return;
1720     
1721     Page* page = frame->page();
1722     if (!page)
1723         return;
1724
1725     NSWindow *window = [self window];
1726     BOOL windowIsKey = [window isKeyWindow];
1727     BOOL windowOrSheetIsKey = windowIsKey || [[window attachedSheet] isKeyWindow];
1728
1729     BOOL isActive = !_private->resigningFirstResponder && windowIsKey && [self _web_firstResponderCausesFocusDisplay];
1730     frame->setIsActive(isActive);            
1731
1732     Frame* focusedFrame = page->focusController()->focusedOrMainFrame();
1733     frame->setWindowHasFocus(frame == focusedFrame && windowOrSheetIsKey);
1734 }
1735
1736 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1737 {
1738     ASSERT([self _hasSelection]);
1739     NSArray *types = [self pasteboardTypesForSelection];
1740
1741     // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1742     NSAttributedString *attributedString = [self selectedAttributedString];
1743     NSMutableArray *mutableTypes = nil;
1744     if (![attributedString containsAttachments]) {
1745         mutableTypes = [types mutableCopy];
1746         [mutableTypes removeObject:NSRTFDPboardType];
1747         types = mutableTypes;
1748     }
1749
1750     [pasteboard declareTypes:types owner:[self _topHTMLView]];
1751     [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1752     [mutableTypes release];
1753 }
1754
1755 - (void)close
1756 {
1757     // Check for a nil _private here incase we were created with initWithCoder. In that case, the WebView is just throwing
1758     // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
1759     if (!_private || _private->closed)
1760         return;
1761     [self _cancelUpdateMouseoverTimer];
1762     [self _cancelUpdateActiveStateTimer];
1763     [self _clearLastHitViewIfSelf];
1764     // FIXME: This is slow; should remove individual observers instead.
1765     [[NSNotificationCenter defaultCenter] removeObserver:self];
1766     [_private->pluginController destroyAllPlugins];
1767     [_private->pluginController setDataSource:nil];
1768     // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1769     [self removeAllToolTips];
1770     [_private clear];
1771     _private->closed = YES;
1772     Page* page = core([self _webView]);
1773     if (page)
1774         page->dragController()->setDraggingImageURL(KURL());
1775 }
1776
1777 - (BOOL)_hasHTMLDocument
1778 {
1779     Frame* coreFrame = core([self _frame]);
1780     if (!coreFrame)
1781         return NO;
1782     Document* document = coreFrame->document();
1783     return document && document->isHTMLDocument();
1784 }
1785
1786 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1787                                                  forType:(NSString *)pboardType
1788                                                inContext:(DOMRange *)context
1789                                             subresources:(NSArray **)subresources
1790 {
1791     if (pboardType == WebArchivePboardType) {
1792         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1793         if (subresources)
1794             *subresources = [archive subresources];
1795         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1796         [archive release];
1797         return fragment;
1798     }
1799     if (pboardType == NSFilenamesPboardType)
1800         return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1801         
1802     if (pboardType == NSHTMLPboardType) {
1803         NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1804         // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1805         if ([HTMLString hasPrefix:@"Version:"]) {
1806             NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
1807             if (range.location != NSNotFound)
1808                 HTMLString = [HTMLString substringFromIndex:range.location];
1809         }
1810         if ([HTMLString length] == 0)
1811             return nil;
1812         
1813         return [[self _bridge] documentFragmentWithMarkupString:HTMLString baseURLString:nil];
1814     }
1815
1816     // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
1817     // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
1818     // FIXME: Remove this once bug 5052369 is fixed.
1819     if ([self _hasHTMLDocument] && pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType) {
1820         NSAttributedString *string = nil;
1821         if (pboardType == NSRTFDPboardType)
1822             string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
1823         if (string == nil)
1824             string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
1825         if (string == nil)
1826             return nil;
1827             
1828         NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
1829             [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
1830             self, @"WebResourceHandler", nil];
1831         NSArray *s;
1832         
1833         BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
1834         if (!wasDeferringCallbacks)
1835             [[self _webView] setDefersCallbacks:YES];
1836             
1837         DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length]) 
1838                                                           document:[[self _frame] DOMDocument] 
1839                                                 documentAttributes:documentAttributes
1840                                                       subresources:&s];
1841         if (subresources)
1842             *subresources = s;
1843         
1844         NSEnumerator *e = [s objectEnumerator];
1845         WebResource *r;
1846         while ((r = [e nextObject]))
1847             [[self _dataSource] addSubresource:r];
1848         
1849         if (!wasDeferringCallbacks)
1850             [[self _webView] setDefersCallbacks:NO];
1851         
1852         [documentAttributes release];
1853         [string release];
1854         return fragment;
1855     }
1856     if (pboardType == NSTIFFPboardType) {
1857         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
1858                                                               URL:uniqueURLWithRelativePart(@"image.tiff")
1859                                                          MIMEType:@"image/tiff" 
1860                                                  textEncodingName:nil
1861                                                         frameName:nil];
1862         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
1863         [resource release];
1864         return fragment;
1865     }
1866     if (pboardType == NSPICTPboardType) {
1867         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
1868                                                               URL:uniqueURLWithRelativePart(@"image.pict")
1869                                                          MIMEType:@"image/pict" 
1870                                                  textEncodingName:nil
1871                                                         frameName:nil];
1872         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
1873         [resource release];
1874         return fragment;
1875     }
1876     if (pboardType == NSURLPboardType) {
1877         NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
1878         DOMDocument* document = [[self _frame] DOMDocument];
1879         ASSERT(document);
1880         if (!document)
1881             return nil;
1882         DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
1883         NSString *URLString = [URL _web_originalDataAsString];
1884         if ([URLString length] == 0)
1885             return nil;
1886         NSString *URLTitleString = [pasteboard stringForType:WebURLNamePboardType];
1887         DOMText *text = [document createTextNode:URLTitleString];
1888         [anchor setHref:URLString];
1889         [anchor appendChild:text];
1890         DOMDocumentFragment *fragment = [document createDocumentFragment];
1891         [fragment appendChild:anchor];
1892         return fragment;
1893     }
1894     if (pboardType == NSStringPboardType)
1895         return [[self _bridge] documentFragmentWithText:[pasteboard stringForType:NSStringPboardType]
1896                                               inContext:context];
1897                                               
1898     return nil;
1899 }
1900
1901 @end
1902
1903 @implementation NSView (WebHTMLViewFileInternal)
1904
1905 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
1906 {
1907     unsigned count = [_subviews count];
1908     for (unsigned i = 0; i < count; ++i) {
1909         NSView *child = [_subviews objectAtIndex:i];
1910         if ([child isKindOfClass:[WebHTMLView class]])
1911             [array addObject:child];
1912         [child _web_addDescendantWebHTMLViewsToArray:array];
1913     }
1914 }
1915
1916 @end
1917
1918 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
1919
1920 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
1921 {
1922     if (object == nil) {
1923         [self removeObjectForKey:key];
1924     } else {
1925         [self setObject:object forKey:key];
1926     }
1927 }
1928
1929 @end
1930
1931 #ifdef BUILDING_ON_TIGER
1932
1933 // The following is a workaround for
1934 // <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears
1935 // The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:.
1936 // Since the category will be searched before the real class, we'll prevent the flag from being
1937 // set on the tool tip panel.
1938
1939 @interface NSToolTipPanel : NSPanel
1940 @end
1941
1942 @interface NSToolTipPanel (WebHTMLViewFileInternal)
1943 @end
1944
1945 @implementation NSToolTipPanel (WebHTMLViewFileInternal)
1946
1947 - (void)setAcceptsMouseMovedEvents:(BOOL)flag
1948 {
1949     // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events.
1950 }
1951
1952 @end
1953
1954 #endif
1955
1956 @interface NSArray (WebHTMLView)
1957 - (void)_web_makePluginViewsPerformSelector:(SEL)selector withObject:(id)object;
1958 @end
1959
1960 @implementation WebHTMLView
1961
1962 + (void)initialize
1963 {
1964     [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] 
1965                              returnTypes:[[self class] _insertablePasteboardTypes]];
1966 #ifndef BUILDING_ON_TIGER
1967     WebCoreObjCFinalizeOnMainThread(self);
1968 #endif
1969 }
1970
1971 - (id)initWithFrame:(NSRect)frame
1972 {
1973     self = [super initWithFrame:frame];
1974     if (!self)
1975         return nil;
1976     
1977     [self setFocusRingType:NSFocusRingTypeNone];
1978     
1979     // Make all drawing go through us instead of subviews.
1980     [self _setDrawsOwnDescendants:YES];
1981     
1982     _private = [[WebHTMLViewPrivate alloc] init];
1983
1984     _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
1985     _private->needsLayout = YES;
1986     
1987     return self;
1988 }
1989
1990 - (void)dealloc
1991 {
1992     // We can't assert that close has already been called because
1993     // this view can be removed from it's superview, even though
1994     // it could be needed later, so close if needed.
1995     [self close];
1996     [_private release];
1997     _private = nil;
1998     [super dealloc];
1999 }
2000
2001 - (void)finalize
2002 {
2003     ASSERT_MAIN_THREAD();
2004     // We can't assert that close has already been called because
2005     // this view can be removed from it's superview, even though
2006     // it could be needed later, so close if needed.
2007     [self close];
2008     [super finalize];
2009 }
2010
2011 // Returns YES if the delegate returns YES (so we should do no more work).
2012 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2013 {
2014     BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2015     _private->selectorForDoCommandBySelector = 0;
2016     if (callerAlreadyCalledDelegate)
2017         return NO;
2018     WebView *webView = [self _webView];
2019     return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2020 }
2021
2022 static String commandNameForSelector(SEL selector)
2023 {
2024     // Change a few command names into ones supported by WebCore::Editor.
2025     // If this list gets too long we might decide we need to use a hash table.
2026     if (selector == @selector(insertParagraphSeparator:) || selector == @selector(insertNewlineIgnoringFieldEditor:))
2027         return "InsertNewline";
2028     if (selector == @selector(insertTabIgnoringFieldEditor:))
2029         return "InsertTab";
2030     if (selector == @selector(pageDown:))
2031         return "MovePageDown";
2032     if (selector == @selector(pageDownAndModifySelection:))
2033         return "MovePageDownAndModifySelection";
2034     if (selector == @selector(pageUp:))
2035         return "MovePageUp";
2036     if (selector == @selector(pageUpAndModifySelection:))
2037         return "MovePageUpAndModifySelection";
2038
2039     // Remove the trailing colon.
2040     const char* selectorName = sel_getName(selector);
2041     size_t selectorNameLength = strlen(selectorName);
2042     ASSERT(selectorNameLength >= 2);
2043     ASSERT(selectorName[selectorNameLength - 1] == ':');
2044     return String(selectorName, selectorNameLength - 1);
2045 }
2046
2047 - (Editor::Command)coreCommandBySelector:(SEL)selector
2048 {
2049     Frame* coreFrame = core([self _frame]);
2050     if (!coreFrame)
2051         return Editor::Command();
2052     return coreFrame->editor()->command(commandNameForSelector(selector));
2053 }
2054
2055 - (Editor::Command)coreCommandByName:(const char*)name
2056 {
2057     Frame* coreFrame = core([self _frame]);
2058     if (!coreFrame)
2059         return Editor::Command();
2060     return coreFrame->editor()->command(name);
2061 }
2062
2063 - (void)executeCoreCommandBySelector:(SEL)selector
2064 {
2065     if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2066         return;
2067     [self coreCommandBySelector:selector].execute();
2068 }
2069
2070 - (void)executeCoreCommandByName:(const char*)name
2071 {
2072     [self coreCommandByName:name].execute();
2073 }
2074
2075 // These commands are forwarded to the Editor object in WebCore.
2076 // Ideally we'd do this for all editing commands; more of the code
2077 // should be moved from here to there, and more commands should be
2078 // added to this list.
2079
2080 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2081 // The functions are identical.
2082
2083 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2084
2085 WEBCORE_COMMAND(alignCenter)
2086 WEBCORE_COMMAND(alignJustified)
2087 WEBCORE_COMMAND(alignLeft)
2088 WEBCORE_COMMAND(alignRight)
2089 WEBCORE_COMMAND(copy)
2090 WEBCORE_COMMAND(cut)
2091 WEBCORE_COMMAND(delete)
2092 WEBCORE_COMMAND(deleteBackward)
2093 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2094 WEBCORE_COMMAND(deleteForward)
2095 WEBCORE_COMMAND(deleteToBeginningOfLine)
2096 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2097 WEBCORE_COMMAND(deleteToEndOfLine)
2098 WEBCORE_COMMAND(deleteToEndOfParagraph)
2099 WEBCORE_COMMAND(deleteToMark)
2100 WEBCORE_COMMAND(deleteWordBackward)
2101 WEBCORE_COMMAND(deleteWordForward)
2102 WEBCORE_COMMAND(indent)
2103 WEBCORE_COMMAND(insertBacktab)
2104 WEBCORE_COMMAND(insertLineBreak)
2105 WEBCORE_COMMAND(insertNewline)
2106 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2107 WEBCORE_COMMAND(insertParagraphSeparator)
2108 WEBCORE_COMMAND(insertTab)
2109 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2110 WEBCORE_COMMAND(moveBackward)
2111 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2112 WEBCORE_COMMAND(moveDown)
2113 WEBCORE_COMMAND(moveDownAndModifySelection)
2114 WEBCORE_COMMAND(moveForward)
2115 WEBCORE_COMMAND(moveForwardAndModifySelection)
2116 WEBCORE_COMMAND(moveLeft)
2117 WEBCORE_COMMAND(moveLeftAndModifySelection)
2118 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2119 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2120 WEBCORE_COMMAND(moveRight)
2121 WEBCORE_COMMAND(moveRightAndModifySelection)
2122 WEBCORE_COMMAND(moveToBeginningOfDocument)
2123 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2124 WEBCORE_COMMAND(moveToBeginningOfLine)
2125 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2126 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2127 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2128 WEBCORE_COMMAND(moveToBeginningOfSentence)
2129 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2130 WEBCORE_COMMAND(moveToEndOfDocument)
2131 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2132 WEBCORE_COMMAND(moveToEndOfLine)
2133 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2134 WEBCORE_COMMAND(moveToEndOfParagraph)
2135 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2136 WEBCORE_COMMAND(moveToEndOfSentence)
2137 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2138 WEBCORE_COMMAND(moveUp)
2139 WEBCORE_COMMAND(moveUpAndModifySelection)
2140 WEBCORE_COMMAND(moveWordBackward)
2141 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2142 WEBCORE_COMMAND(moveWordForward)
2143 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2144 WEBCORE_COMMAND(moveWordLeft)
2145 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2146 WEBCORE_COMMAND(moveWordRight)
2147 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2148 WEBCORE_COMMAND(outdent)
2149 WEBCORE_COMMAND(pageDown)
2150 WEBCORE_COMMAND(pageDownAndModifySelection)
2151 WEBCORE_COMMAND(pageUp)
2152 WEBCORE_COMMAND(pageUpAndModifySelection)
2153 WEBCORE_COMMAND(selectAll)
2154 WEBCORE_COMMAND(selectLine)
2155 WEBCORE_COMMAND(selectParagraph)
2156 WEBCORE_COMMAND(selectSentence)
2157 WEBCORE_COMMAND(selectToMark)
2158 WEBCORE_COMMAND(selectWord)
2159 WEBCORE_COMMAND(setMark)
2160 WEBCORE_COMMAND(subscript)
2161 WEBCORE_COMMAND(superscript)
2162 WEBCORE_COMMAND(swapWithMark)
2163 WEBCORE_COMMAND(transpose)
2164 WEBCORE_COMMAND(underline)
2165 WEBCORE_COMMAND(unscript)
2166 WEBCORE_COMMAND(yank)
2167 WEBCORE_COMMAND(yankAndSelect)
2168
2169 #undef WEBCORE_COMMAND
2170
2171 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2172
2173 - (IBAction)takeFindStringFromSelection:(id)sender
2174 {
2175     COMMAND_PROLOGUE
2176
2177     if (![self _hasSelection]) {
2178         NSBeep();
2179         return;
2180     }
2181
2182     [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2183 }
2184
2185 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2186 {
2187     [pasteboard declareTypes:types owner:[self _topHTMLView]];
2188     [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2189     return YES;
2190 }
2191
2192 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard
2193 {
2194     Frame* coreFrame = core([self _frame]);
2195     if (!coreFrame)
2196         return NO;
2197     if (coreFrame->selectionController()->isContentRichlyEditable())
2198         [self _pasteWithPasteboard:pasteboard allowPlainText:YES];
2199     else
2200         [self _pasteAsPlainTextWithPasteboard:pasteboard];
2201     return YES;
2202 }
2203
2204 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2205 {
2206     if (sendType != nil && [[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]) {
2207         return self;
2208     } else if (returnType != nil && [[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2209         return self;
2210     }
2211     return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2212 }
2213
2214 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2215 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2216 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2217 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2218 // might be using the selector, and we don't want to break them.
2219 - (void)jumpToSelection:(id)sender
2220 {
2221     COMMAND_PROLOGUE
2222
2223     if (Frame* coreFrame = core([self _frame]))
2224         coreFrame->revealSelection(RenderLayer::gAlignCenterAlways);
2225 }
2226
2227 - (NSCellStateValue)selectionHasStyle:(CSSStyleDeclaration*)style
2228 {
2229     Frame* coreFrame = core([self _frame]);
2230     if (!coreFrame)
2231         return NSOffState;
2232     return kit(coreFrame->editor()->selectionHasStyle(style));
2233 }
2234
2235 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2236 {
2237     SEL action = [item action];
2238     RefPtr<Frame> frame = core([self _frame]);
2239
2240     if (!frame)
2241         return NO;
2242     
2243     if (Document* doc = frame->document()) {
2244         if (doc->isPluginDocument())
2245             return NO;
2246         if (doc->isImageDocument()) {            
2247             if (action == @selector(copy:))
2248                 return frame->loader()->isComplete();
2249             return NO;
2250         }
2251     }
2252
2253     if (action == @selector(changeSpelling:)
2254             || action == @selector(_changeSpellingFromMenu:)
2255             || action == @selector(checkSpelling:)
2256             || action == @selector(complete:)
2257             || action == @selector(pasteFont:))
2258         return [self _canEdit];
2259     
2260     if (action == @selector(showGuessPanel:)) {
2261 #ifndef BUILDING_ON_TIGER
2262         // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2263         NSMenuItem *menuItem = (NSMenuItem *)item;
2264         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2265             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2266             [menuItem setTitle:panelShowing ? UI_STRING("Hide Spelling and Grammar", "menu item title") : UI_STRING("Show Spelling and Grammar", "menu item title")];
2267         }
2268 #endif
2269         return [self _canEdit];
2270     }
2271     
2272     if (action == @selector(changeBaseWritingDirection:)) {
2273         NSWritingDirection writingDirection = static_cast<NSWritingDirection>([item tag]);
2274         if (writingDirection == NSWritingDirectionNatural)
2275             return NO;
2276         NSMenuItem *menuItem = (NSMenuItem *)item;
2277         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2278             RefPtr<CSSStyleDeclaration> style = new CSSMutableStyleDeclaration;
2279             ExceptionCode ec;
2280             style->setProperty("direction", writingDirection == NSWritingDirectionLeftToRight ? "LTR" : "RTL", ec);
2281             [menuItem setState:frame->editor()->selectionHasStyle(style.get())];
2282         }
2283         return [self _canEdit];
2284     }
2285     
2286     if (action == @selector(toggleBaseWritingDirection:)) {
2287         NSMenuItem *menuItem = (NSMenuItem *)item;
2288         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2289             RefPtr<CSSStyleDeclaration> style = new CSSMutableStyleDeclaration;
2290             ExceptionCode ec;
2291             style->setProperty("direction", "RTL", ec);
2292             // Take control of the title of the menu item, instead of just checking/unchecking it because otherwise
2293             // we don't know what the check would mean.
2294             [menuItem setTitle:frame->editor()->selectionHasStyle(style.get())
2295                 ? UI_STRING("Left to Right", "Left to Right context menu item")
2296                 : UI_STRING("Right to Left", "Right to Left context menu item")];
2297         }
2298         return [self _canEdit];
2299     } 
2300     
2301     if (action == @selector(changeAttributes:)
2302             || action == @selector(changeColor:)        
2303             || action == @selector(changeFont:))
2304         return [self _canEditRichly];
2305     
2306     if (action == @selector(capitalizeWord:)
2307                || action == @selector(lowercaseWord:)
2308                || action == @selector(uppercaseWord:))
2309         return [self _hasSelection] && [self _isEditable];
2310     
2311     if (action == @selector(centerSelectionInVisibleArea:)
2312                || action == @selector(jumpToSelection:)
2313                || action == @selector(copyFont:))
2314         return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2315     
2316     if (action == @selector(changeDocumentBackgroundColor:))
2317         return [[self _webView] isEditable] && [self _canEditRichly];
2318     
2319     if (action == @selector(_ignoreSpellingFromMenu:)
2320             || action == @selector(_learnSpellingFromMenu:)
2321             || action == @selector(takeFindStringFromSelection:))
2322         return [self _hasSelection];
2323     
2324     if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2325         return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste());
2326     
2327     if (action == @selector(pasteAsRichText:))
2328         return frame && (frame->editor()->canDHTMLPaste()
2329             || (frame->editor()->canPaste() && frame->selectionController()->isContentRichlyEditable()));
2330     
2331     if (action == @selector(performFindPanelAction:))
2332         return NO;
2333     
2334     if (action == @selector(_lookUpInDictionaryFromMenu:))
2335         return [self _hasSelection];
2336     
2337 #ifndef BUILDING_ON_TIGER
2338     if (action == @selector(toggleGrammarChecking:)) {
2339         // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate 
2340         // the selector here because we implement it here, and we must implement it here because the AppKit 
2341         // code checks the first responder.
2342         NSMenuItem *menuItem = (NSMenuItem *)item;
2343         if ([menuItem isKindOfClass:[NSMenuItem class]])
2344             [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2345         return YES;
2346     }
2347 #endif
2348     
2349     Editor::Command command = [self coreCommandBySelector:action];
2350     if (command.isSupported()) {
2351         NSMenuItem *menuItem = (NSMenuItem *)item;
2352         if ([menuItem isKindOfClass:[NSMenuItem class]])
2353             [menuItem setState:kit(command.state())];
2354         return command.isEnabled();
2355     }
2356
2357     return YES;
2358 }
2359
2360 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2361 {
2362     BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2363     return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2364 }
2365
2366 - (BOOL)acceptsFirstResponder
2367 {
2368     // Don't accept first responder when we first click on this view.
2369     // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2370     // Do accept first responder at any other time, for example from keyboard events,
2371     // or from calls back from WebCore once we begin mouse-down event handling.
2372     NSEvent *event = [NSApp currentEvent];
2373     if ([event type] == NSLeftMouseDown
2374             && !_private->handlingMouseDownEvent
2375             && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2376         return NO;
2377     }
2378     return YES;
2379 }
2380
2381 - (BOOL)maintainsInactiveSelection
2382 {
2383     // This method helps to determine whether the WebHTMLView should maintain
2384     // an inactive selection when it's not first responder.
2385     // Traditionally, these views have not maintained such selections,
2386     // clearing them when the view was not first responder. However,
2387     // to fix bugs like this one:
2388     // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even 
2389     //                            when they're not firstResponder"
2390     // it was decided to add a switch to act more like an NSTextView.
2391
2392     if ([[self _webView] maintainsInactiveSelection])
2393         return YES;
2394
2395     // Predict the case where we are losing first responder status only to
2396     // gain it back again. Want to keep the selection in that case.
2397     id nextResponder = [[self window] _newFirstResponderAfterResigning];
2398     if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2399         id contentView = [nextResponder contentView];
2400         if (contentView)
2401             nextResponder = contentView;
2402     }
2403     if ([nextResponder isKindOfClass:[NSClipView class]]) {
2404         id documentView = [nextResponder documentView];
2405         if (documentView)
2406             nextResponder = documentView;
2407     }
2408     if (nextResponder == self)
2409         return YES;
2410
2411     Frame* coreFrame = core([self _frame]);
2412     bool selectionIsEditable = coreFrame && coreFrame->selectionController()->isContentEditable();
2413     bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2414         && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2415
2416     return selectionIsEditable && nextResponderIsInWebView;
2417 }
2418
2419 - (void)addMouseMovedObserver
2420 {
2421     if (!_private->dataSource || ![self _isTopHTMLView])
2422         return;
2423
2424     // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2425     if (!([[self window] isKeyWindow] || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]))
2426         return;
2427
2428     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2429         name:WKMouseMovedNotification() object:nil];
2430     [self _frameOrBoundsChanged];
2431 }
2432
2433 - (void)removeMouseMovedObserverUnconditionally
2434 {
2435     [[NSNotificationCenter defaultCenter] removeObserver:self
2436         name:WKMouseMovedNotification() object:nil];
2437 }
2438
2439 - (void)removeMouseMovedObserver
2440 {
2441     // Don't remove the observer if we're running the Dashboard.
2442     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2443         return;
2444
2445     [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2446     [self removeMouseMovedObserverUnconditionally];
2447 }
2448
2449 - (void)addSuperviewObservers
2450 {
2451     // We watch the bounds of our superview, so that we can do a layout when the size
2452     // of the superview changes. This is different from other scrollable things that don't
2453     // need this kind of thing because their layout doesn't change.
2454     
2455     // We need to pay attention to both height and width because our "layout" has to change
2456     // to extend the background the full height of the space and because some elements have
2457     // sizes that are based on the total size of the view.
2458     
2459     NSView *superview = [self superview];
2460     if (superview && [self window]) {
2461         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
2462             name:NSViewFrameDidChangeNotification object:superview];
2463         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_frameOrBoundsChanged) 
2464             name:NSViewBoundsDidChangeNotification object:superview];
2465
2466         // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2467         // It will check the current size/scroll against the previous layout's size/scroll.  We need to
2468         // do this here to catch the case where the WebView is laid out at one size, removed from its
2469         // window, resized, and inserted into another window.  Our frame/bounds changed notifications
2470         // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2471         [self _frameOrBoundsChanged];
2472     }
2473 }
2474
2475 - (void)removeSuperviewObservers
2476 {
2477     NSView *superview = [self superview];
2478     if (superview && [self window]) {
2479         [[NSNotificationCenter defaultCenter] removeObserver:self
2480             name:NSViewFrameDidChangeNotification object:superview];
2481         [[NSNotificationCenter defaultCenter] removeObserver:self
2482             name:NSViewBoundsDidChangeNotification object:superview];
2483     }
2484 }
2485
2486 - (void)addWindowObservers
2487 {
2488     NSWindow *window = [self window];
2489     if (window) {
2490         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:)
2491             name:NSWindowDidBecomeKeyNotification object:nil];
2492         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:)
2493             name:NSWindowDidResignKeyNotification object:nil];
2494         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:)
2495             name:NSWindowWillCloseNotification object:window];
2496     }
2497 }
2498
2499 - (void)removeWindowObservers
2500 {
2501     NSWindow *window = [self window];
2502     if (window) {
2503         [[NSNotificationCenter defaultCenter] removeObserver:self
2504             name:NSWindowDidBecomeKeyNotification object:nil];
2505         [[NSNotificationCenter defaultCenter] removeObserver:self
2506             name:NSWindowDidResignKeyNotification object:nil];
2507         [[NSNotificationCenter defaultCenter] removeObserver:self
2508             name:NSWindowWillCloseNotification object:window];
2509     }
2510 }
2511
2512 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2513 {
2514     [self removeSuperviewObservers];
2515 }
2516
2517 - (void)viewDidMoveToSuperview
2518 {
2519     // Do this here in case the text size multiplier changed when a non-HTML
2520     // view was installed.
2521     if ([self superview] != nil) {
2522         [self _updateTextSizeMultiplier];
2523         [self addSuperviewObservers];
2524     }
2525 }
2526
2527 static void _updateActiveStateTimerCallback(CFRunLoopTimerRef timer, void *info)
2528 {
2529     WebHTMLView *view = (WebHTMLView *)info;
2530     [view _updateActiveState];
2531 }
2532
2533 - (void)viewWillMoveToWindow:(NSWindow *)window
2534 {
2535     // Don't do anything if we aren't initialized.  This happens
2536     // when decoding a WebView.  When WebViews are decoded their subviews
2537     // are created by initWithCoder: and so won't be normally
2538     // initialized.  The stub views are discarded by WebView.
2539     if (!_private)
2540         return;
2541
2542     // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2543     [self removeMouseMovedObserverUnconditionally];
2544     [self removeWindowObservers];
2545     [self removeSuperviewObservers];
2546     [self _cancelUpdateMouseoverTimer];
2547     [self _cancelUpdateActiveStateTimer];
2548     
2549     [[self _pluginController] stopAllPlugins];
2550 }
2551
2552 - (void)viewDidMoveToWindow
2553 {
2554     // Don't do anything if we aren't initialized.  This happens
2555     // when decoding a WebView.  When WebViews are decoded their subviews
2556     // are created by initWithCoder: and so won't be normally
2557     // initialized.  The stub views are discarded by WebView.
2558     if (!_private || _private->closed)
2559         return;
2560         
2561     [self _stopAutoscrollTimer];
2562     if ([self window]) {
2563         _private->lastScrollPosition = [[self superview] bounds].origin;
2564         [self addWindowObservers];
2565         [self addSuperviewObservers];
2566         [self addMouseMovedObserver];
2567
2568         // Schedule this update, rather than making the call right now.
2569         // The reason is that placing the caret in the just-installed view requires
2570         // the HTML/XML document to be available on the WebCore side, but it is not
2571         // at the time this code is running. However, it will be there on the next
2572         // crank of the run loop. Doing this helps to make a blinking caret appear 
2573         // in a new, empty window "automatic".
2574         if (!_private->updateActiveStateTimer) {
2575             CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
2576             _private->updateActiveStateTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0,
2577                                                                     _updateActiveStateTimerCallback, &context);
2578             CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateActiveStateTimer, kCFRunLoopDefaultMode);
2579         }
2580         
2581         [[self _pluginController] startAllPlugins];
2582
2583         _private->lastScrollPosition = NSZeroPoint;
2584     }
2585 }
2586
2587 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2588 {
2589     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2590 }
2591
2592 - (void)viewDidMoveToHostWindow
2593 {
2594     [[self subviews] _web_makePluginViewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2595 }
2596
2597
2598 - (void)addSubview:(NSView *)view
2599 {
2600     [super addSubview:view];
2601
2602     if ([WebPluginController isPlugInView:view])
2603         [[self _pluginController] addPlugin:view];
2604 }
2605
2606 - (void)willRemoveSubview:(NSView *)subview
2607 {
2608     if ([WebPluginController isPlugInView:subview])
2609         [[self _pluginController] destroyPlugin:subview];
2610
2611     [super willRemoveSubview:subview];
2612 }
2613
2614 - (void)reapplyStyles
2615 {
2616     if (!_private->needsToApplyStyles) {
2617         return;
2618     }
2619     
2620 #ifdef _KWQ_TIMING        
2621     double start = CFAbsoluteTimeGetCurrent();
2622 #endif
2623
2624     [[self _bridge] reapplyStylesForDeviceType:
2625         _private->printing ? WebCoreDevicePrinter : WebCoreDeviceScreen];
2626     
2627 #ifdef _KWQ_TIMING        
2628     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2629     LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2630 #endif
2631
2632     _private->needsToApplyStyles = NO;
2633 }
2634
2635 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2636 // minPageWidth==0 implies a non-printing layout
2637 - (void)layoutToMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize
2638 {
2639     [self reapplyStyles];
2640     
2641     if (!_private->needsLayout && ![[self _bridge] needsLayout])
2642         return;
2643
2644 #ifdef _KWQ_TIMING        
2645     double start = CFAbsoluteTimeGetCurrent();
2646 #endif
2647
2648     LOG(View, "%@ doing layout", self);
2649
2650     if (minPageWidth > 0.0) {
2651         [[self _bridge] forceLayoutWithMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
2652     } else {
2653         [[self _bridge] forceLayoutAdjustingViewSize:adjustViewSize];
2654     }
2655     _private->needsLayout = NO;
2656     
2657     if (!_private->printing)
2658         _private->lastLayoutSize = [(NSClipView *)[self superview] documentVisibleRect].size;
2659
2660 #ifdef _KWQ_TIMING        
2661     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2662     LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
2663 #endif
2664 }
2665
2666 - (void)layout
2667 {
2668     [self layoutToMinimumPageWidth:0.0f maximumPageWidth:0.0f adjustingViewSize:NO];
2669 }
2670
2671 - (NSMenu *)menuForEvent:(NSEvent *)event
2672 {
2673     [_private->compController endRevertingChange:NO moveLeft:NO];
2674
2675     _private->handlingMouseDownEvent = YES;
2676     BOOL handledEvent = NO;
2677     Frame* coreFrame = core([self _frame]);
2678
2679     if (!coreFrame) {
2680         _private->handlingMouseDownEvent = NO;
2681         return nil;
2682     }
2683
2684     Page* page = coreFrame->page();
2685     if (!page)
2686         return nil;
2687
2688     page->contextMenuController()->clearContextMenu();
2689     // Match behavior of other browsers by sending an onmousedown event for right clicks.
2690     coreFrame->eventHandler()->mouseDown(event);
2691     handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformMouseEvent(event));
2692     _private->handlingMouseDownEvent = NO;
2693
2694     if (!handledEvent)
2695         return nil;
2696
2697     ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
2698     if (!coreMenu)
2699         return nil;
2700
2701     NSArray* menuItems = coreMenu->platformDescription();
2702     NSMenu* menu = nil;
2703     if (menuItems && [menuItems count] > 0) {
2704         menu = [[[NSMenu alloc] init] autorelease];
2705         for (unsigned i = 0; i < [menuItems count]; i++)
2706             [menu addItem:[menuItems objectAtIndex:i]];
2707     }
2708
2709     return menu;
2710 }
2711
2712 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
2713 {
2714     return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
2715 }
2716
2717 - (void)clearFocus
2718 {
2719     Frame* coreFrame = core([self _frame]);
2720     if (!coreFrame)
2721         return;
2722     Document* document = coreFrame->document();
2723     if (!document)
2724         return;
2725     
2726     document->setFocusedNode(0);
2727 }
2728
2729 - (BOOL)isOpaque
2730 {
2731     return [[self _webView] drawsBackground];
2732 }
2733
2734 - (void)setNeedsDisplay:(BOOL)flag
2735 {
2736     LOG(View, "%@ flag = %d", self, (int)flag);
2737     [super setNeedsDisplay: flag];
2738 }
2739
2740 - (void)setNeedsLayout: (BOOL)flag
2741 {
2742     LOG(View, "%@ flag = %d", self, (int)flag);
2743     _private->needsLayout = flag;
2744 }
2745
2746
2747 - (void)setNeedsToApplyStyles: (BOOL)flag
2748 {
2749     LOG(View, "%@ flag = %d", self, (int)flag);
2750     _private->needsToApplyStyles = flag;
2751 }
2752
2753 - (void)drawSingleRect:(NSRect)rect
2754 {
2755     [NSGraphicsContext saveGraphicsState];
2756     NSRectClip(rect);
2757         
2758     ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
2759
2760     [(WebClipView *)[self superview] setAdditionalClip:rect];
2761
2762     @try {
2763         if ([self _transparentBackground]) {
2764             [[NSColor clearColor] set];
2765             NSRectFill (rect);
2766         }
2767
2768         [[self _bridge] drawRect:rect];
2769
2770         // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
2771         // called after the WebView has closed. If the client did not properly close the WebView and set the 
2772         // UIDelegate to nil, then the UIDelegate will be stale and this code will crash. 
2773         static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
2774         if (version3OrLaterClient) {
2775             WebView *webView = [self _webView];
2776             [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
2777         }
2778
2779         [(WebClipView *)[self superview] resetAdditionalClip];
2780
2781         [NSGraphicsContext restoreGraphicsState];
2782     } @catch (NSException *localException) {
2783         [(WebClipView *)[self superview] resetAdditionalClip];
2784         [NSGraphicsContext restoreGraphicsState];
2785         LOG_ERROR("Exception caught while drawing: %@", localException);
2786         [localException raise];
2787     }
2788 }
2789
2790 - (void)drawRect:(NSRect)rect
2791 {
2792     ASSERT_MAIN_THREAD();
2793     LOG(View, "%@ drawing", self);
2794
2795     const NSRect *rects;
2796     NSInteger count;
2797     [self getRectsBeingDrawn:&rects count:&count];
2798
2799     BOOL subviewsWereSetAside = _private->subviewsSetAside;
2800     if (subviewsWereSetAside)
2801         [self _restoreSubviews];
2802
2803 #ifdef _KWQ_TIMING
2804     double start = CFAbsoluteTimeGetCurrent();
2805 #endif
2806
2807     // If count == 0 here, use the rect passed in for drawing. This is a workaround for:
2808     // <rdar://problem/3908282> REGRESSION (Mail): No drag image dragging selected text in Blot and Mail
2809     // The reason for the workaround is that this method is called explicitly from the code
2810     // to generate a drag image, and at that time, getRectsBeingDrawn:count: will return a zero count.
2811     const int cRectThreshold = 10;
2812     const float cWastedSpaceThreshold = 0.75f;
2813     BOOL useUnionedRect = (count <= 1) || (count > cRectThreshold);
2814     if (!useUnionedRect) {
2815         // Attempt to guess whether or not we should use the unioned rect or the individual rects.
2816         // We do this by computing the percentage of "wasted space" in the union.  If that wasted space
2817         // is too large, then we will do individual rect painting instead.
2818         float unionPixels = (rect.size.width * rect.size.height);
2819         float singlePixels = 0;
2820         for (int i = 0; i < count; ++i)
2821             singlePixels += rects[i].size.width * rects[i].size.height;
2822         float wastedSpace = 1 - (singlePixels / unionPixels);
2823         if (wastedSpace <= cWastedSpaceThreshold)
2824             useUnionedRect = YES;
2825     }
2826     
2827     if (useUnionedRect)
2828         [self drawSingleRect:rect];
2829     else
2830         for (int i = 0; i < count; ++i)
2831             [self drawSingleRect:rects[i]];
2832
2833 #ifdef _KWQ_TIMING
2834     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2835     LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
2836 #endif
2837
2838     if (subviewsWereSetAside)
2839         [self _setAsideSubviews];
2840 }
2841
2842 // Turn off the additional clip while computing our visibleRect.
2843 - (NSRect)visibleRect
2844 {
2845     if (!([[self superview] isKindOfClass:[WebClipView class]]))
2846         return [super visibleRect];
2847         
2848     WebClipView *clipView = (WebClipView *)[self superview];
2849
2850     BOOL hasAdditionalClip = [clipView hasAdditionalClip];
2851     if (!hasAdditionalClip) {
2852         return [super visibleRect];
2853     }
2854     
2855     NSRect additionalClip = [clipView additionalClip];
2856     [clipView resetAdditionalClip];
2857     NSRect visibleRect = [super visibleRect];
2858     [clipView setAdditionalClip:additionalClip];
2859     return visibleRect;
2860 }
2861
2862 - (BOOL)isFlipped 
2863 {
2864     return YES;
2865 }
2866
2867 - (void)windowDidBecomeKey:(NSNotification *)notification
2868 {
2869     NSWindow *keyWindow = [notification object];
2870
2871     if (keyWindow == [self window])
2872         [self addMouseMovedObserver];
2873
2874     if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet])
2875         [self _updateActiveState];
2876 }
2877
2878 - (void)windowDidResignKey:(NSNotification *)notification
2879 {
2880     NSWindow *formerKeyWindow = [notification object];
2881
2882     if (formerKeyWindow == [self window])
2883         [self removeMouseMovedObserver];
2884
2885     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
2886         [self _updateActiveState];
2887         [_private->compController endRevertingChange:NO moveLeft:NO];
2888     }
2889 }
2890
2891 - (void)windowWillClose:(NSNotification *)notification
2892 {
2893     [_private->compController endRevertingChange:NO moveLeft:NO];
2894     [[self _pluginController] destroyAllPlugins];
2895 }
2896
2897 - (void)scrollWheel:(NSEvent *)event
2898 {
2899     [self retain];
2900     Frame* frame = core([self _frame]);
2901     if (!frame || !frame->eventHandler()->wheelEvent(event))
2902         [super scrollWheel:event];
2903     [self release];
2904 }
2905
2906 - (BOOL)_isSelectionEvent:(NSEvent *)event
2907 {
2908     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
2909     return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
2910 }
2911
2912 - (BOOL)acceptsFirstMouse:(NSEvent *)event
2913 {
2914     NSView *hitView = [self _hitViewForEvent:event];
2915     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2916     
2917     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
2918         return YES;
2919     
2920     if (hitHTMLView) {
2921         bool result = false;
2922         if (Frame* coreFrame = core([hitHTMLView _frame])) {
2923             coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
2924             [hitHTMLView _setMouseDownEvent:event];
2925             if ([hitHTMLView _isSelectionEvent:event])
2926                 result = coreFrame->eventHandler()->eventMayStartDrag(event);
2927             [hitHTMLView _setMouseDownEvent:nil];
2928         }
2929         return result;
2930     }
2931     return [hitView acceptsFirstMouse:event];
2932 }
2933
2934 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
2935 {
2936     NSView *hitView = [self _hitViewForEvent:event];
2937     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
2938     if (hitHTMLView) {
2939         bool result = false;
2940         if ([hitHTMLView _isSelectionEvent:event])
2941             if (Frame* coreFrame = core([hitHTMLView _frame])) {
2942                 [hitHTMLView _setMouseDownEvent:event];
2943                 result = coreFrame->eventHandler()->eventMayStartDrag(event);
2944                 [hitHTMLView _setMouseDownEvent:nil];
2945             }
2946         return result;
2947     }
2948     return [hitView shouldDelayWindowOrderingForEvent:event];
2949 }
2950
2951 - (void)mouseDown:(NSEvent *)event
2952 {
2953     RetainPtr<WebHTMLView> protector = self;
2954     if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
2955         return;
2956
2957     _private->handlingMouseDownEvent = YES;
2958
2959     // Record the mouse down position so we can determine drag hysteresis.
2960     [self _setMouseDownEvent:event];
2961
2962     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
2963     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
2964         goto done;
2965
2966     [_private->compController endRevertingChange:NO moveLeft:NO];
2967
2968     // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
2969     // We don't want to pass them along to KHTML a second time.
2970     if (!([event modifierFlags] & NSControlKeyMask)) {
2971         _private->ignoringMouseDraggedEvents = NO;
2972
2973         // Don't do any mouseover while the mouse is down.
2974         [self _cancelUpdateMouseoverTimer];
2975
2976         // Let WebCore get a chance to deal with the event. This will call back to us
2977         // to start the autoscroll timer if appropriate.
2978         if (Frame* coreframe = core([self _frame]))
2979             coreframe->eventHandler()->mouseDown(event);
2980     }
2981
2982 done:
2983     _private->handlingMouseDownEvent = NO;
2984 }
2985
2986 - (void)dragImage:(NSImage *)dragImage
2987                at:(NSPoint)at
2988            offset:(NSSize)offset
2989             event:(NSEvent *)event
2990        pasteboard:(NSPasteboard *)pasteboard
2991            source:(id)source
2992         slideBack:(BOOL)slideBack
2993 {
2994     ASSERT(self == [self _topHTMLView]);
2995     [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
2996 }
2997
2998 - (void)mouseDragged:(NSEvent *)event
2999 {
3000     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3001     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3002         return;
3003
3004     [self retain];
3005
3006     if (!_private->ignoringMouseDraggedEvents)
3007         if (Frame* coreframe = core([self _frame]))
3008             coreframe->eventHandler()->mouseDragged(event);
3009
3010     [self release];
3011 }
3012
3013 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
3014 {
3015     ASSERT(![self _webView] || [self _isTopHTMLView]);
3016     
3017     Page *page = core([self _webView]);
3018     
3019     if (!page)
3020         return NSDragOperationNone;
3021     
3022     if (page->dragController()->dragOperation() == DragOperationNone)
3023         return NSDragOperationGeneric | NSDragOperationCopy;
3024     
3025     return (NSDragOperation)page->dragController()->dragOperation();
3026 }
3027
3028 - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenLoc
3029 {
3030     ASSERT(![self _webView] || [self _isTopHTMLView]);
3031     
3032     NSPoint windowImageLoc = [[self window] convertScreenToBase:screenLoc];
3033     NSPoint windowMouseLoc = windowImageLoc;
3034     
3035     if (Page* page = core([self _webView])) {
3036         DragController* dragController = page->dragController();
3037         NSPoint windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3038     }
3039     
3040     [[self _bridge] dragSourceMovedTo:windowMouseLoc];
3041 }
3042
3043 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
3044 {
3045     ASSERT(![self _webView] || [self _isTopHTMLView]);
3046     
3047     NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
3048     NSPoint windowMouseLoc = windowImageLoc;
3049     
3050     if (Page* page = core([self _webView])) {
3051         DragController* dragController = page->dragController();
3052         windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3053         dragController->dragEnded();
3054     }
3055     
3056     [[self _bridge] dragSourceEndedAt:windowMouseLoc operation:operation];
3057     
3058     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3059     _private->ignoringMouseDraggedEvents = YES;
3060     
3061     // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
3062     // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
3063     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
3064                                             location:windowMouseLoc
3065                                        modifierFlags:[[NSApp currentEvent] modifierFlags]
3066                                            timestamp:[NSDate timeIntervalSinceReferenceDate]
3067                                         windowNumber:[[self window] windowNumber]
3068                                              context:[[NSApp currentEvent] context]
3069                                          eventNumber:0 clickCount:0 pressure:0];
3070     [self mouseUp:fakeEvent]; // This will also update the mouseover state.
3071 }
3072
3073 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3074 {
3075     NSFileWrapper *wrapper = nil;
3076     
3077     if (WebCore::CachedResource* tiffResource = [self promisedDragTIFFDataSource]) {
3078         
3079         SharedBuffer *buffer = tiffResource->data();
3080         if (!buffer)
3081             goto noPromisedData;
3082         
3083         NSData *data = buffer->createNSData();
3084         NSURLResponse *response = tiffResource->response().nsURLResponse();
3085         
3086         wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
3087         [wrapper setPreferredFilename:[response suggestedFilename]];
3088     }
3089     
3090 noPromisedData:
3091     
3092     if (!wrapper) {
3093         ASSERT(![self _webView] || [self _isTopHTMLView]);
3094         Page* page = core([self _webView]);
3095         
3096         //If a load occurs midway through a drag, the view may be detached, which gives
3097         //us no ability to get to the original Page, so we cannot access any drag state
3098         //FIXME: is there a way to recover?
3099         if (!page) 
3100             return nil; 
3101         
3102         KURL imageURL = page->dragController()->draggingImageURL();
3103         ASSERT(!imageURL.isEmpty());
3104         
3105         wrapper = [[self _dataSource] _fileWrapperForURL:imageURL.getNSURL()];
3106     }
3107     
3108     if (wrapper == nil) {
3109         LOG_ERROR("Failed to create image file.");
3110         return nil;
3111     }
3112
3113     // FIXME: Report an error if we fail to create a file.
3114     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3115     path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
3116     if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
3117         LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
3118
3119     return [NSArray arrayWithObject:[path lastPathComponent]];
3120 }
3121
3122 - (void)mouseUp:(NSEvent *)event
3123 {
3124     [self _setMouseDownEvent:nil];
3125
3126     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3127     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3128         return;
3129
3130     [self retain];
3131
3132     [self _stopAutoscrollTimer];
3133     if (Frame* coreframe = core([self _frame]))
3134         coreframe->eventHandler()->mouseUp(event);
3135     [self _updateMouseoverWithFakeEvent];
3136
3137     [self release];
3138 }
3139
3140 - (void)mouseMovedNotification:(NSNotification *)notification
3141 {
3142     [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
3143 }
3144
3145 // returning YES from this method is the way we tell AppKit that it is ok for this view
3146 // to be in the key loop even when "tab to all controls" is not on.
3147 - (BOOL)needsPanelToBecomeKey
3148 {
3149     return YES;
3150 }
3151
3152 - (BOOL)becomeFirstResponder
3153 {
3154     NSSelectionDirection direction = NSDirectSelection;
3155     if (![[self _webView] _isPerformingProgrammaticFocus])
3156         direction = [[self window] keyViewSelectionDirection];
3157
3158     [self _updateActiveState];
3159     [self _updateFontPanel];
3160     
3161     Frame* frame = core([self _frame]);
3162     if (!frame)
3163         return YES;
3164     
3165     frame->editor()->setStartNewKillRingSequence(true);
3166
3167     if (direction == NSDirectSelection)
3168         return YES;
3169
3170     Page* page = frame->page();
3171     if (!page)
3172         return YES;
3173
3174     page->focusController()->setFocusedFrame(frame);
3175     if (Document* document = frame->document())
3176         document->setFocusedNode(0);
3177     page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward,
3178                                              frame->eventHandler()->currentKeyboardEvent().get());
3179     return YES;
3180 }
3181
3182 - (BOOL)resignFirstResponder
3183 {
3184     BOOL resign = [super resignFirstResponder];
3185     if (resign) {
3186         [_private->compController endRevertingChange:NO moveLeft:NO];
3187         _private->resigningFirstResponder = YES;
3188         if (![self maintainsInactiveSelection]) { 
3189             [self deselectAll];
3190             if (![[self _webView] _isPerformingProgrammaticFocus])
3191                 [self clearFocus];
3192         }
3193         [self _updateActiveState];
3194         _private->resigningFirstResponder = NO;
3195     }
3196     return resign;
3197 }
3198
3199 - (void)setDataSource:(WebDataSource *)dataSource 
3200 {
3201     ASSERT(dataSource);
3202     if (_private->dataSource != dataSource) {
3203         ASSERT(!_private->closed);
3204         BOOL hadDataSource = _private->dataSource != nil;
3205
3206         [dataSource retain];
3207         [_private->dataSource release];
3208         _private->dataSource = dataSource;
3209         [_private->pluginController setDataSource:dataSource];
3210
3211         if (!hadDataSource)
3212             [self addMouseMovedObserver];
3213     }
3214 }
3215
3216 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3217 {
3218 }
3219
3220 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3221 // key.  WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3222 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3223 - (void)updateCell:(NSCell*)cell
3224 {
3225 }
3226
3227 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3228 // pageWidth != 0 implies we will relayout to a new width
3229 - (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize
3230 {
3231     WebFrame *frame = [self _frame];
3232     NSArray *subframes = [frame childFrames];
3233     unsigned n = [subframes count];
3234     unsigned i;
3235     for (i = 0; i != n; ++i) {
3236         WebFrame *subframe = [subframes objectAtIndex:i];
3237         WebFrameView *frameView = [subframe frameView];
3238         if ([[subframe _dataSource] _isDocumentHTML]) {
3239             [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:adjustViewSize];
3240         }
3241     }
3242
3243     if (printing != _private->printing) {
3244         [_private->pageRects release];
3245         _private->pageRects = nil;
3246         _private->printing = printing;
3247         if (!printing)
3248             _private->avoidingPrintOrphan = NO;
3249         [self setNeedsToApplyStyles:YES];
3250         [self setNeedsLayout:YES];
3251         [self layoutToMinimumPageWidth:minPageWidth maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize];
3252         if (!printing) {
3253             // Can't do this when starting printing or nested printing won't work, see 3491427.
3254             [self setNeedsDisplay:NO];
3255         }
3256     }
3257 }
3258
3259 - (BOOL)canPrintHeadersAndFooters
3260 {
3261     return YES;
3262 }
3263
3264 // This is needed for the case where the webview is embedded in the view that's being printed.
3265 // It shouldn't be called when the webview is being printed directly.
3266 - (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit
3267 {
3268     // This helps when we print as part of a larger print process.
3269     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3270     BOOL wasInPrintingMode = _private->printing;
3271     if (!wasInPrintingMode)
3272         [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3273
3274     [[self _bridge] adjustPageHeightNew:newBottom top:oldTop bottom:oldBottom limit:bottomLimit];
3275     
3276     if (!wasInPrintingMode) {
3277         NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation];
3278         if (currenPrintOperation)
3279             // delay _setPrinting:NO until back to main loop as this method may get called repeatedly
3280             [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0];
3281         else
3282             // not sure if this is actually ever invoked, it probably shouldn't be
3283             [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3284     }
3285 }
3286
3287 - (float)_availablePaperWidthForPrintOperation:(NSPrintOperation *)printOperation
3288 {
3289     NSPrintInfo *printInfo = [printOperation printInfo];
3290     return [printInfo paperSize].width - [printInfo leftMargin] - [printInfo rightMargin];
3291 }
3292
3293 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3294 {
3295     float viewWidth = NSWidth([self bounds]);
3296     if (viewWidth < 1) {
3297         LOG_ERROR("%@ has no width when printing", self);
3298         return 1.0f;
3299     }
3300
3301     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3302     float maxShrinkToFitScaleFactor = 1.0f / PrintingMaximumShrinkFactor;
3303     float shrinkToFitScaleFactor = [self _availablePaperWidthForPrintOperation:printOperation]/viewWidth;
3304     float shrinkToAvoidOrphan = _private->avoidingPrintOrphan ? (1.0f / PrintingOrphanShrinkAdjustment) : 1.0f;
3305     return userScaleFactor * MAX(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor) * shrinkToAvoidOrphan;
3306 }
3307
3308 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3309 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3310 // if AppKit makes it SPI/API.
3311 - (float)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation 
3312 {
3313     return [self _scaleFactorForPrintOperation:printOperation];
3314 }
3315
3316 // This is used for Carbon printing. At some point we might want to make this public API.
3317 - (void)setPageWidthForPrinting:(float)pageWidth
3318 {
3319     [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
3320     [self _setPrinting:YES minimumPageWidth:pageWidth maximumPageWidth:pageWidth adjustViewSize:YES];
3321 }
3322
3323 - (void)_endPrintMode
3324 {
3325     [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
3326     [[self window] setAutodisplay:YES];
3327 }
3328
3329 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3330 {
3331     ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3332     NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3333     if (initiatingOperation == currentOperation) {
3334         // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3335         // being extra paranoid here since the printing code is so fragile. Delay the cleanup
3336         // further.
3337         ASSERT_NOT_REACHED();
3338         [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3339     } else if ([currentOperation view] == self) {
3340         // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3341         // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3342         // fragile. Do nothing, because we don't want to break the print job currently in progress, and
3343         // the print job currently in progress is responsible for its own cleanup.
3344         ASSERT_NOT_REACHED();
3345     } else {
3346         // The print job that kicked off this delayed call has finished, and this view is not being
3347         // printed again. We expect that no other print job has started. Since this delayed call wasn't
3348         // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3349         // the print mode here.
3350         ASSERT(currentOperation == nil);
3351         [self _endPrintMode];
3352     }
3353 }
3354
3355 // Return the number of pages available for printing
3356 - (BOOL)knowsPageRange:(NSRangePointer)range
3357 {
3358     // Must do this explicit display here, because otherwise the view might redisplay while the print
3359     // sheet was up, using printer fonts (and looking different).
3360     [self displayIfNeeded];
3361     [[self window] setAutodisplay:NO];
3362     
3363     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
3364     // according to the paper size
3365     float minLayoutWidth = 0.0f;
3366     float maxLayoutWidth = 0.0f;
3367     Frame* frame = core([self _frame]);
3368     if (!frame)
3369         return NO;
3370     if (!frame->isFrameSet()) {
3371         float paperWidth = [self _availablePaperWidthForPrintOperation:[NSPrintOperation currentOperation]];
3372         minLayoutWidth = paperWidth * PrintingMinimumShrinkFactor;
3373         maxLayoutWidth = paperWidth * PrintingMaximumShrinkFactor;
3374     }
3375     [self _setPrinting:YES minimumPageWidth:minLayoutWidth maximumPageWidth:maxLayoutWidth adjustViewSize:YES]; // will relayout
3376     NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3377     // Certain types of errors, including invalid page ranges, can cause beginDocument and
3378     // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3379     // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3380     // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3381     // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3382     // which is after beginDocument and endDocument would be called.
3383     [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3384     [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3385     
3386     // There is a theoretical chance that someone could do some drawing between here and endDocument,
3387     // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3388     // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3389
3390     range->location = 1;
3391     float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3392     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3393     [_private->pageRects release];
3394     float fullPageHeight = floorf([self _calculatePrintHeight]/totalScaleFactor);
3395     NSArray *newPageRects = [[self _bridge] computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3396                                                                           printHeight:fullPageHeight];
3397     
3398     // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3399     // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3400     // a blank page (with correct-looking header and footer if that option is on), which matches
3401     // the behavior of IE and Camino at least.
3402     if ([newPageRects count] == 0)
3403         newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3404     else if ([newPageRects count] > 1) {
3405         // If the last page is a short orphan, try adjusting the print height slightly to see if this will squeeze the
3406         // content onto one fewer page. If it does, use the adjusted scale. If not, use the original scale.
3407         float lastPageHeight = NSHeight([[newPageRects lastObject] rectValue]);
3408         if (lastPageHeight/fullPageHeight < LastPrintedPageOrphanRatio) {
3409             NSArray *adjustedPageRects = [[self _bridge] computePageRectsWithPrintWidthScaleFactor:userScaleFactor
3410                                                                                        printHeight:fullPageHeight*PrintingOrphanShrinkAdjustment];
3411             // Use the adjusted rects only if the page count went down
3412             if ([adjustedPageRects count] < [newPageRects count]) {
3413                 newPageRects = adjustedPageRects;
3414                 _private->avoidingPrintOrphan = YES;
3415             }
3416         }
3417     }
3418     
3419     _private->pageRects = [newPageRects retain];
3420     
3421     range->length = [_private->pageRects count];
3422     
3423     return YES;
3424 }
3425
3426 // Return the drawing rectangle for a particular page number
3427 - (NSRect)rectForPage:(int)page
3428 {
3429     return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3430 }
3431
3432 - (void)drawPageBorderWithSize:(NSSize)borderSize
3433 {
3434     ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));    
3435     [[self _webView] _drawHeaderAndFooter];
3436 }
3437
3438 - (void)beginDocument
3439 {
3440     @try {
3441         // From now on we'll get a chance to call _endPrintMode in either beginDocument or
3442         // endDocument, so we can cancel the "just in case" pending call.
3443         [NSObject cancelPreviousPerformRequestsWithTarget:self
3444                                                  selector:@selector(_delayedEndPrintMode:)
3445                                                    object:[NSPrintOperation currentOperation]];
3446         [super beginDocument];
3447     } @catch (NSException *localException) {
3448         // Exception during [super beginDocument] means that endDocument will not get called,
3449         // so we need to clean up our "print mode" here.
3450         [self _endPrintMode];
3451     }
3452 }
3453
3454 - (void)endDocument
3455 {
3456     [super endDocument];
3457     // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO 
3458     [self _endPrintMode];
3459 }
3460
3461 - (void)keyDown:(NSEvent *)event
3462 {
3463     RetainPtr<WebHTMLView> selfProtector = self;
3464     BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
3465
3466     BOOL callSuper = NO;
3467
3468     [_private->keyDownEvent release];
3469     _private->keyDownEvent = [event retain];
3470
3471     BOOL completionPopupWasOpen = _private->compController && [_private->compController popupWindowIsOpen];
3472     Frame* coreFrame = core([self _frame]);
3473     if (!eventWasSentToWebCore && coreFrame && coreFrame->eventHandler()->keyEvent(event)) {
3474         // WebCore processed a key event, bail on any preexisting complete: UI
3475         if (completionPopupWasOpen)
3476             [_private->compController endRevertingChange:YES moveLeft:NO];
3477     } else if (!_private->compController || ![_private->compController filterKeyDown:event]) {
3478         // Not consumed by complete: popup window
3479         [_private->compController endRevertingChange:YES moveLeft:NO];
3480         callSuper = YES;
3481     }
3482     if (callSuper)
3483         [super keyDown:event];
3484     else
3485         [NSCursor setHiddenUntilMouseMoves:YES];
3486 }
3487
3488 - (void)keyUp:(NSEvent *)event
3489 {
3490     BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
3491
3492     [self retain];
3493     Frame* coreFrame = core([self _frame]);
3494     if (eventWasSentToWebCore || !coreFrame || !coreFrame->eventHandler()->keyEvent(event))
3495         [super keyUp:event];    
3496     [self release];
3497 }
3498
3499 - (void)flagsChanged:(NSEvent *)event
3500 {
3501     Frame* coreFrame = core([self _frame]);
3502     if (coreFrame)
3503         coreFrame->eventHandler()->capsLockStateMayHaveChanged();
3504     
3505     RetainPtr<WebHTMLView> selfProtector = self;
3506