initial import
[vuplus_webkit] / Source / WebKit / mac / WebView / WebHTMLView.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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 "DOMCSSStyleDeclarationInternal.h"
33 #import "DOMDocumentFragmentInternal.h"
34 #import "DOMDocumentInternal.h"
35 #import "DOMNodeInternal.h"
36 #import "DOMRangeInternal.h"
37 #import "WebArchive.h"
38 #import "WebClipView.h"
39 #import "WebDOMOperationsInternal.h"
40 #import "WebDataSourceInternal.h"
41 #import "WebDefaultUIDelegate.h"
42 #import "WebDelegateImplementationCaching.h"
43 #import "WebDocumentInternal.h"
44 #import "WebDynamicScrollBarsViewInternal.h"
45 #import "WebEditingDelegate.h"
46 #import "WebElementDictionary.h"
47 #import "WebFrameInternal.h"
48 #import "WebFramePrivate.h"
49 #import "WebFrameViewInternal.h"
50 #import "WebHTMLRepresentationPrivate.h"
51 #import "WebHTMLViewInternal.h"
52 #import "WebKitLogging.h"
53 #import "WebKitNSStringExtras.h"
54 #import "WebKitVersionChecks.h"
55 #import "WebLocalizableStringsInternal.h"
56 #import "WebNSEventExtras.h"
57 #import "WebNSFileManagerExtras.h"
58 #import "WebNSImageExtras.h"
59 #import "WebNSObjectExtras.h"
60 #import "WebNSPasteboardExtras.h"
61 #import "WebNSPrintOperationExtras.h"
62 #import "WebNSURLExtras.h"
63 #import "WebNSViewExtras.h"
64 #import "WebNetscapePluginView.h"
65 #import "WebNodeHighlight.h"
66 #import "WebPluginController.h"
67 #import "WebPreferences.h"
68 #import "WebPreferencesPrivate.h"
69 #import "WebResourcePrivate.h"
70 #import "WebTextCompletionController.h"
71 #import "WebTypesInternal.h"
72 #import "WebUIDelegatePrivate.h"
73 #import "WebViewInternal.h"
74 #import <AppKit/NSAccessibility.h>
75 #import <ApplicationServices/ApplicationServices.h>
76 #import <WebCore/CSSMutableStyleDeclaration.h>
77 #import <WebCore/CachedImage.h>
78 #import <WebCore/CachedResourceClient.h>
79 #import <WebCore/CachedResourceLoader.h>
80 #import <WebCore/Chrome.h>
81 #import <WebCore/ColorMac.h>
82 #import <WebCore/ContextMenu.h>
83 #import <WebCore/ContextMenuController.h>
84 #import <WebCore/Document.h>
85 #import <WebCore/DocumentFragment.h>
86 #import <WebCore/DocumentMarkerController.h>
87 #import <WebCore/DragController.h>
88 #import <WebCore/Editor.h>
89 #import <WebCore/EditorDeleteAction.h>
90 #import <WebCore/Element.h>
91 #import <WebCore/EventHandler.h>
92 #import <WebCore/ExceptionHandlers.h>
93 #import <WebCore/FloatRect.h>
94 #import <WebCore/FocusController.h>
95 #import <WebCore/Frame.h>
96 #import <WebCore/FrameLoader.h>
97 #import <WebCore/FrameSelection.h>
98 #import <WebCore/FrameView.h>
99 #import <WebCore/HTMLConverter.h>
100 #import <WebCore/HTMLNames.h>
101 #import <WebCore/HitTestResult.h>
102 #import <WebCore/Image.h>
103 #import <WebCore/KeyboardEvent.h>
104 #import <WebCore/LegacyWebArchive.h>
105 #import <WebCore/MIMETypeRegistry.h>
106 #import <WebCore/Page.h>
107 #import <WebCore/PlatformKeyboardEvent.h>
108 #import <WebCore/Range.h>
109 #import <WebCore/RenderWidget.h>
110 #import <WebCore/RenderView.h>
111 #import <WebCore/RuntimeApplicationChecks.h>
112 #import <WebCore/SharedBuffer.h>
113 #import <WebCore/SimpleFontData.h>
114 #import <WebCore/Text.h>
115 #import <WebCore/WebCoreObjCExtras.h>
116 #import <WebCore/WebFontCache.h>
117 #import <WebCore/WebNSAttributedStringExtras.h>
118 #import <WebCore/markup.h>
119 #import <WebKit/DOM.h>
120 #import <WebKit/DOMExtensions.h>
121 #import <WebKit/DOMPrivate.h>
122 #import <WebKitSystemInterface.h>
123 #import <dlfcn.h>
124 #import <limits>
125 #import <runtime/InitializeThreading.h>
126 #import <wtf/MainThread.h>
127
128 #if USE(ACCELERATED_COMPOSITING)
129 #import <QuartzCore/QuartzCore.h>
130 #endif
131
132 using namespace WebCore;
133 using namespace HTMLNames;
134 using namespace WTF;
135 using namespace std;
136
137 @interface WebMenuTarget : NSObject {
138     WebCore::ContextMenuController* _menuController;
139 }
140 + (WebMenuTarget*)sharedMenuTarget;
141 - (WebCore::ContextMenuController*)menuController;
142 - (void)setMenuController:(WebCore::ContextMenuController*)menuController;
143 - (void)forwardContextMenuAction:(id)sender;
144 - (BOOL)validateMenuItem:(NSMenuItem *)item;
145 @end
146
147 static WebMenuTarget* target;
148
149 @implementation WebMenuTarget
150
151 + (WebMenuTarget*)sharedMenuTarget
152 {
153     if (!target)
154         target = [[WebMenuTarget alloc] init];
155     return target;
156 }
157
158 - (WebCore::ContextMenuController*)menuController
159 {
160     return _menuController;
161 }
162
163 - (void)setMenuController:(WebCore::ContextMenuController*)menuController
164 {
165     _menuController = menuController;
166 }
167
168 - (void)forwardContextMenuAction:(id)sender
169 {
170     WebCore::ContextMenuItem item(WebCore::ActionType, static_cast<WebCore::ContextMenuAction>([sender tag]), [sender title]);
171     _menuController->contextMenuItemSelected(&item);
172 }
173
174 - (BOOL)validateMenuItem:(NSMenuItem *)item
175 {
176     WebCore::ContextMenuItem coreItem(item);
177     ASSERT(_menuController->contextMenu());
178     _menuController->checkOrEnableIfNeeded(coreItem);
179     return coreItem.enabled();
180 }
181
182 @end
183
184 @interface NSWindow (BorderViewAccess)
185 - (NSView*)_web_borderView;
186 @end
187
188 @implementation NSWindow (BorderViewAccess)
189 - (NSView*)_web_borderView
190 {
191     return _borderView;
192 }
193 @end
194
195 @interface WebResponderChainSink : NSResponder {
196     NSResponder* _lastResponderInChain;
197     BOOL _receivedUnhandledCommand;
198 }
199 - (id)initWithResponderChain:(NSResponder *)chain;
200 - (void)detach;
201 - (BOOL)receivedUnhandledCommand;
202 @end
203
204 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
205 static BOOL forceNSViewHitTest;
206
207 // if YES, do the "top WebHTMLView" hit test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721])
208 static BOOL forceWebHTMLViewHitTest;
209
210 static WebHTMLView *lastHitView;
211
212 static bool needsCursorRectsSupportAtPoint(NSWindow* window, NSPoint point)
213 {
214     forceNSViewHitTest = YES;
215     NSView* view = [[window _web_borderView] hitTest:point];
216     forceNSViewHitTest = NO;
217
218     // WebHTMLView doesn't use cursor rects.
219     if ([view isKindOfClass:[WebHTMLView class]])
220         return false;
221
222 #if ENABLE(NETSCAPE_PLUGIN_API)
223     // Neither do NPAPI plug-ins.
224     if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
225         return false;
226 #endif
227
228     // Non-Web content, WebPDFView, and WebKit plug-ins use normal cursor handling.
229     return true;
230 }
231
232
233 static IMP oldSetCursorForMouseLocationIMP;
234
235 // Overriding an internal method is a hack; <rdar://problem/7662987> tracks finding a better solution.
236 static void setCursor(NSWindow *self, SEL cmd, NSPoint point)
237 {
238     if (needsCursorRectsSupportAtPoint(self, point))
239         oldSetCursorForMouseLocationIMP(self, cmd, point);
240 }
241
242
243 extern "C" {
244
245 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
246
247 extern NSString *NSMarkedClauseSegmentAttributeName;
248 extern NSString *NSTextInputReplacementRangeAttributeName;
249
250 }
251
252 @interface NSView (WebNSViewDetails)
253 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
254 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
255 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView;
256 - (NSRect)_dirtyRect;
257 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
258 - (BOOL)_drawnByAncestor;
259 - (void)_invalidateGStatesForTree;
260 - (void)_propagateDirtyRectsToOpaqueAncestors;
261 - (void)_windowChangedKeyState;
262 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
263 - (void)_updateLayerGeometryFromView;
264 #endif
265 @end
266
267 #if USE(ACCELERATED_COMPOSITING)
268 static IMP oldSetNeedsDisplayInRectIMP;
269
270 static void setNeedsDisplayInRect(NSView *self, SEL cmd, NSRect invalidRect)
271 {
272     if (![self _drawnByAncestor]) {
273         oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
274         return;
275     }
276
277     static Class webFrameViewClass = [WebFrameView class];
278     WebFrameView *enclosingWebFrameView = (WebFrameView *)self;
279     while (enclosingWebFrameView && ![enclosingWebFrameView isKindOfClass:webFrameViewClass])
280         enclosingWebFrameView = (WebFrameView *)[enclosingWebFrameView superview];
281
282     if (!enclosingWebFrameView) {
283         oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
284         return;
285     }
286
287     Frame* coreFrame = core([enclosingWebFrameView webFrame]);
288     FrameView* frameView = coreFrame ? coreFrame->view() : 0;
289     if (!frameView || !frameView->isEnclosedInCompositingLayer()) {
290         oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect);
291         return;
292     }
293
294     NSRect invalidRectInWebFrameViewCoordinates = [enclosingWebFrameView convertRect:invalidRect fromView:self];
295     IntRect invalidRectInFrameViewCoordinates(invalidRectInWebFrameViewCoordinates);
296     if (![enclosingWebFrameView isFlipped])
297         invalidRectInFrameViewCoordinates.setY(frameView->frameRect().size().height() - invalidRectInFrameViewCoordinates.maxY());
298
299     frameView->invalidateRect(invalidRectInFrameViewCoordinates);
300 }
301 #endif // USE(ACCELERATED_COMPOSITING)
302
303 @interface NSApplication (WebNSApplicationDetails)
304 - (void)speakString:(NSString *)string;
305 @end
306
307 @interface NSWindow (WebNSWindowDetails)
308 - (id)_newFirstResponderAfterResigning;
309 @end
310
311 @interface NSAttributedString (WebNSAttributedStringDetails)
312 - (id)_initWithDOMRange:(DOMRange *)range;
313 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
314 @end
315
316 @interface NSSpellChecker (WebNSSpellCheckerDetails)
317 - (void)learnWord:(NSString *)word;
318 @end
319
320 // By imaging to a width a little wider than the available pixels,
321 // thin pages will be scaled down a little, matching the way they
322 // print in IE and Camino. This lets them use fewer sheets than they
323 // would otherwise, which is presumably why other browsers do this.
324 // Wide pages will be scaled down more than this.
325 const float _WebHTMLViewPrintingMinimumShrinkFactor = 1.25;
326
327 // This number determines how small we are willing to reduce the page content
328 // in order to accommodate the widest line. If the page would have to be
329 // reduced smaller to make the widest line fit, we just clip instead (this
330 // behavior matches MacIE and Mozilla, at least)
331 const float _WebHTMLViewPrintingMaximumShrinkFactor = 2;
332
333 #define AUTOSCROLL_INTERVAL             0.1f
334
335 // Any non-zero value will do, but using something recognizable might help us debug some day.
336 #define TRACKING_RECT_TAG 0xBADFACE
337
338 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
339 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
340
341 #define STANDARD_WEIGHT 5
342 #define MIN_BOLD_WEIGHT 7
343 #define STANDARD_BOLD_WEIGHT 9
344
345 // Fake URL scheme.
346 #define WebDataProtocolScheme @"webkit-fake-url"
347
348 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
349 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
350 @interface WebCoreScrollView : NSScrollView
351 @end
352
353 @implementation WebCoreScrollView
354 @end
355
356 // We need this to be able to safely reference the CachedImage for the promised drag data
357 static CachedResourceClient* promisedDataClient()
358 {
359     static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient;
360     return staticCachedResourceClient;
361 }
362
363 @interface WebHTMLView (WebHTMLViewFileInternal)
364 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
365 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
366 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
367 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
368 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
369 - (void)_removeMouseMovedObserverUnconditionally;
370 - (void)_removeSuperviewObservers;
371 - (void)_removeWindowObservers;
372 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
373 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
374 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
375 - (DOMRange *)_selectedRange;
376 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
377 - (NSView *)_hitViewForEvent:(NSEvent *)event;
378 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
379 - (DOMRange *)_documentRange;
380 - (void)_setMouseDownEvent:(NSEvent *)event;
381 - (WebHTMLView *)_topHTMLView;
382 - (BOOL)_isTopHTMLView;
383 - (void)_web_setPrintingModeRecursive;
384 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
385 - (void)_web_clearPrintingModeRecursive;
386 @end
387
388 #ifndef BUILDING_ON_LEOPARD
389
390 @interface WebHTMLView (WebHTMLViewTextCheckingInternal)
391 - (void)orderFrontSubstitutionsPanel:(id)sender;
392 - (BOOL)smartInsertDeleteEnabled;
393 - (void)setSmartInsertDeleteEnabled:(BOOL)flag;
394 - (void)toggleSmartInsertDelete:(id)sender;
395 - (BOOL)isAutomaticQuoteSubstitutionEnabled;
396 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag;
397 - (void)toggleAutomaticQuoteSubstitution:(id)sender;
398 - (BOOL)isAutomaticLinkDetectionEnabled;
399 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag;
400 - (void)toggleAutomaticLinkDetection:(id)sender;
401 - (BOOL)isAutomaticDashSubstitutionEnabled;
402 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag;
403 - (void)toggleAutomaticDashSubstitution:(id)sender;
404 - (BOOL)isAutomaticTextReplacementEnabled;
405 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag;
406 - (void)toggleAutomaticTextReplacement:(id)sender;
407 - (BOOL)isAutomaticSpellingCorrectionEnabled;
408 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag;
409 - (void)toggleAutomaticSpellingCorrection:(id)sender;
410 @end
411
412 #endif
413
414 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
415 - (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageWidth logicalHeight:(float)minPageHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent;
416 - (void)_updateSecureInputState;
417 @end
418
419 @class NSTextInputContext;
420 @interface NSResponder (AppKitDetails)
421 - (NSTextInputContext *)inputContext;
422 @end
423
424 @interface NSObject (NSTextInputContextDetails)
425 - (BOOL)wantsToHandleMouseEvents;
426 - (BOOL)handleMouseEvent:(NSEvent *)event;
427 @end
428
429 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
430 - (void)_updateSelectionForInputManager;
431 @end
432
433 @interface WebHTMLView (WebEditingStyleSupport)
434 - (DOMCSSStyleDeclaration *)_emptyStyle;
435 - (NSString *)_colorAsString:(NSColor *)color;
436 @end
437
438 @interface NSView (WebHTMLViewFileInternal)
439 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
440 @end
441
442 @interface NSMutableDictionary (WebHTMLViewFileInternal)
443 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
444 @end
445
446 struct WebHTMLViewInterpretKeyEventsParameters {
447     KeyboardEvent* event;
448     bool eventInterpretationHadSideEffects;
449     bool shouldSaveCommands;
450     bool consumedByIM;
451     bool executingSavedKeypressCommands;
452 };
453
454 @interface WebHTMLViewPrivate : NSObject {
455 @public
456     BOOL closed;
457     BOOL ignoringMouseDraggedEvents;
458     BOOL printing;
459     BOOL paginateScreenContent;
460     BOOL observingMouseMovedNotifications;
461     BOOL observingSuperviewNotifications;
462     BOOL observingWindowNotifications;
463     
464     id savedSubviews;
465     BOOL subviewsSetAside;
466
467 #if USE(ACCELERATED_COMPOSITING)
468     NSView *layerHostingView;
469     BOOL drawingIntoLayer;
470 #endif
471
472     NSEvent *mouseDownEvent; // Kept after handling the event.
473     BOOL handlingMouseDownEvent;
474     NSEvent *keyDownEvent; // Kept after handling the event.
475
476     // A WebHTMLView has a single input context, but we return nil when in non-editable content to avoid making input methods do their work.
477     // This state is saved each time selection changes, because computing it causes style recalc, which is not always safe to do.
478     BOOL exposeInputContext;
479
480     // Track whether the view has set a secure input state.
481     BOOL isInSecureInputState;
482
483     BOOL _forceUpdateSecureInputState;
484
485     NSPoint lastScrollPosition;
486     BOOL inScrollPositionChanged;
487
488     WebPluginController *pluginController;
489     
490     NSString *toolTip;
491     NSToolTipTag lastToolTipTag;
492     id trackingRectOwner;
493     void *trackingRectUserData;
494     
495     NSTimer *autoscrollTimer;
496     NSEvent *autoscrollTriggerEvent;
497     
498     NSArray *pageRects;
499
500     NSMutableDictionary *highlighters;
501
502     
503     WebTextCompletionController *completionController;
504     
505     BOOL transparentBackground;
506
507     WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters;
508     
509     WebDataSource *dataSource;
510     WebCore::CachedImage* promisedDragTIFFDataSource;
511     
512     CFRunLoopTimerRef updateMouseoverTimer;
513
514     SEL selectorForDoCommandBySelector;
515
516 #ifndef NDEBUG
517     BOOL enumeratingSubviews;
518 #endif
519 }
520 - (void)clear;
521 @end
522
523 static NSCellStateValue kit(TriState state)
524 {
525     switch (state) {
526         case FalseTriState:
527             return NSOffState;
528         case TrueTriState:
529             return NSOnState;
530         case MixedTriState:
531             return NSMixedState;
532     }
533     ASSERT_NOT_REACHED();
534     return NSOffState;
535 }
536
537 @implementation WebHTMLViewPrivate
538
539 + (void)initialize
540 {
541     JSC::initializeThreading();
542     WTF::initializeMainThreadToProcessMainThread();
543     WebCoreObjCFinalizeOnMainThread(self);
544     
545     if (!oldSetCursorForMouseLocationIMP) {
546         Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
547         ASSERT(setCursorMethod);
548         oldSetCursorForMouseLocationIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
549         ASSERT(oldSetCursorForMouseLocationIMP);
550     }
551
552 #if USE(ACCELERATED_COMPOSITING)
553     if (!oldSetNeedsDisplayInRectIMP) {
554         Method setNeedsDisplayInRectMethod = class_getInstanceMethod([NSView class], @selector(setNeedsDisplayInRect:));
555         ASSERT(setNeedsDisplayInRectMethod);
556         oldSetNeedsDisplayInRectIMP = method_setImplementation(setNeedsDisplayInRectMethod, (IMP)setNeedsDisplayInRect);
557         ASSERT(oldSetNeedsDisplayInRectIMP);
558     }
559 #endif // USE(ACCELERATED_COMPOSITING)
560
561
562 }
563
564 - (void)dealloc
565 {
566     if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self))
567         return;
568
569     ASSERT(!autoscrollTimer);
570     ASSERT(!autoscrollTriggerEvent);
571     ASSERT(!updateMouseoverTimer);
572     
573     [mouseDownEvent release];
574     [keyDownEvent release];
575     [pluginController release];
576     [toolTip release];
577     [completionController release];
578     [dataSource release];
579     [highlighters release];
580     if (promisedDragTIFFDataSource)
581         promisedDragTIFFDataSource->removeClient(promisedDataClient());
582
583     [super dealloc];
584 }
585
586 - (void)finalize
587 {
588     ASSERT_MAIN_THREAD();
589
590     if (promisedDragTIFFDataSource)
591         promisedDragTIFFDataSource->removeClient(promisedDataClient());
592
593     [super finalize];
594 }
595
596 - (void)clear
597 {
598     [mouseDownEvent release];
599     [keyDownEvent release];
600     [pluginController release];
601     [toolTip release];
602     [completionController release];
603     [dataSource release];
604     [highlighters release];
605     if (promisedDragTIFFDataSource)
606         promisedDragTIFFDataSource->removeClient(promisedDataClient());
607
608     mouseDownEvent = nil;
609     keyDownEvent = nil;
610     pluginController = nil;
611     toolTip = nil;
612     completionController = nil;
613     dataSource = nil;
614     highlighters = nil;
615     promisedDragTIFFDataSource = 0;
616
617 #if USE(ACCELERATED_COMPOSITING)
618     layerHostingView = nil;
619 #endif
620 }
621
622 @end
623
624 @implementation WebHTMLView (WebHTMLViewFileInternal)
625
626 - (DOMRange *)_documentRange
627 {
628     return [[[self _frame] DOMDocument] _documentRange];
629 }
630
631 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
632 {
633     NSEnumerator *enumerator = [paths objectEnumerator];
634     NSString *path;
635     
636     while ((path = [enumerator nextObject]) != nil) {
637         NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]);
638         if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
639             return YES;
640     }
641     
642     return NO;
643 }
644
645 - (WebDataSource *)_dataSource
646 {
647     return _private->dataSource;
648 }
649
650 - (WebView *)_webView
651 {
652     return [_private->dataSource _webView];
653 }
654
655 - (WebFrameView *)_frameView
656 {
657     return [[_private->dataSource webFrame] frameView];
658 }
659
660 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
661 {
662     DOMDocumentFragment *fragment;
663     NSEnumerator *enumerator = [paths objectEnumerator];
664     NSMutableArray *domNodes = [[NSMutableArray alloc] init];
665     NSString *path;
666     
667     while ((path = [enumerator nextObject]) != nil) {
668         // Non-image file types; _web_userVisibleString is appropriate here because this will
669         // be pasted as visible text.
670         NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
671         [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
672     }
673     
674     fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes]; 
675     
676     [domNodes release];
677     
678     return [fragment firstChild] != nil ? fragment : nil;
679 }
680
681 + (NSArray *)_excludedElementsForAttributedStringConversion
682 {
683     static NSArray *elements = nil;
684     if (elements == nil) {
685         elements = [[NSArray alloc] initWithObjects:
686             // Omit style since we want style to be inline so the fragment can be easily inserted.
687             @"style",
688             // Omit xml so the result is not XHTML.
689             @"xml", 
690             // Omit tags that will get stripped when converted to a fragment anyway.
691             @"doctype", @"html", @"head", @"body",
692             // Omit deprecated tags.
693             @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
694             // Omit object so no file attachments are part of the fragment.
695             @"object", nil];
696         CFRetain(elements);
697     }
698     return elements;
699 }
700
701 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
702 {
703     CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
704     NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
705     CFRelease(UUIDRef);
706     NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
707     CFRelease(UUIDString);
708
709     return URL;
710 }
711
712 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
713                                                inContext:(DOMRange *)context
714                                           allowPlainText:(BOOL)allowPlainText
715 {
716     NSArray *types = [pasteboard types];
717     DOMDocumentFragment *fragment = nil;
718
719     if ([types containsObject:WebArchivePboardType] &&
720         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
721                                                   forType:WebArchivePboardType
722                                                 inContext:context
723                                              subresources:0]))
724         return fragment;
725                                            
726     if ([types containsObject:NSFilenamesPboardType] &&
727         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
728                                                   forType:NSFilenamesPboardType
729                                                 inContext:context
730                                              subresources:0]))
731         return fragment;
732     
733     if ([types containsObject:NSHTMLPboardType] &&
734         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
735                                                   forType:NSHTMLPboardType
736                                                 inContext:context
737                                              subresources:0]))
738         return fragment;
739     
740     if ([types containsObject:NSRTFDPboardType] &&
741         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
742                                                   forType:NSRTFDPboardType
743                                                 inContext:context
744                                              subresources:0]))
745         return fragment;
746     
747     if ([types containsObject:NSRTFPboardType] &&
748         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
749                                                   forType:NSRTFPboardType
750                                                 inContext:context
751                                              subresources:0]))
752         return fragment;
753
754     if ([types containsObject:NSTIFFPboardType] &&
755         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
756                                                   forType:NSTIFFPboardType
757                                                 inContext:context
758                                              subresources:0]))
759         return fragment;
760
761     if ([types containsObject:NSPDFPboardType] &&
762         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
763                                                   forType:NSPDFPboardType
764                                                 inContext:context
765                                              subresources:0]))
766         return fragment;
767
768 #ifdef BUILDING_ON_LEOPARD
769     if ([types containsObject:NSPICTPboardType] &&
770         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
771                                                   forType:NSPICTPboardType
772                                                 inContext:context
773                                              subresources:0]))
774         return fragment;
775 #endif
776
777     // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
778     // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
779     if ([types containsObject:(NSString*)kUTTypePNG] &&
780         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
781                                                   forType:(NSString*)kUTTypePNG
782                                                 inContext:context
783                                              subresources:0]))
784         return fragment;
785         
786     if ([types containsObject:NSURLPboardType] &&
787         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
788                                                   forType:NSURLPboardType
789                                                 inContext:context
790                                              subresources:0]))
791         return fragment;
792         
793     if (allowPlainText && [types containsObject:NSStringPboardType] &&
794         (fragment = [self _documentFragmentFromPasteboard:pasteboard
795                                                   forType:NSStringPboardType
796                                                 inContext:context
797                                              subresources:0])) {
798         return fragment;
799     }
800     
801     return nil;
802 }
803
804 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
805 {
806     NSArray *types = [pasteboard types];
807     
808     if ([types containsObject:NSStringPboardType])
809         return [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping];
810     
811     NSAttributedString *attributedString = nil;
812     NSString *string;
813
814     if ([types containsObject:NSRTFDPboardType])
815         attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
816     if (attributedString == nil && [types containsObject:NSRTFPboardType])
817         attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
818     if (attributedString != nil) {
819         string = [[attributedString string] copy];
820         [attributedString release];
821         return [string autorelease];
822     }
823     
824     if ([types containsObject:NSFilenamesPboardType]) {
825         string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
826         if (string != nil)
827             return string;
828     }
829     
830     NSURL *URL;
831     
832     if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
833         string = [URL _web_userVisibleString];
834         if ([string length] > 0)
835             return string;
836     }
837     
838     return nil;
839 }
840
841 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
842 {
843     WebView *webView = [[self _webView] retain];
844     [webView _setInsertionPasteboard:pasteboard];
845
846     DOMRange *range = [self _selectedRange];
847     Frame* coreFrame = core([self _frame]);
848     
849 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
850     DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
851     if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted])
852         coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
853 #else
854     // Mail is ignoring the frament passed to the delegate and creates a new one.
855     // We want to avoid creating the fragment twice.
856     if (applicationIsAppleMail()) {
857         if ([self _shouldInsertFragment:nil replacingDOMRange:range givenAction:WebViewInsertActionPasted]) {
858             DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
859             if (fragment)
860                 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
861         }        
862     } else {
863         DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
864         if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted])
865             coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
866     }
867 #endif
868     [webView _setInsertionPasteboard:nil];
869     [webView release];
870 }
871
872 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard 
873
874     WebView *webView = [[self _webView] retain]; 
875     [webView _setInsertionPasteboard:pasteboard]; 
876
877     NSString *text = [self _plainTextFromPasteboard:pasteboard]; 
878     if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted]) 
879         [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]]; 
880
881     [webView _setInsertionPasteboard:nil]; 
882     [webView release];
883 }
884
885 // This method is needed to support Mac OS X services.
886 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard 
887
888     Frame* coreFrame = core([self _frame]); 
889     if (!coreFrame) 
890         return NO; 
891     if (coreFrame->selection()->isContentRichlyEditable()) 
892         [self _pasteWithPasteboard:pasteboard allowPlainText:YES]; 
893     else 
894         [self _pasteAsPlainTextWithPasteboard:pasteboard]; 
895     return YES; 
896 }
897
898 - (void)_removeMouseMovedObserverUnconditionally
899 {
900     if (!_private || !_private->observingMouseMovedNotifications)
901         return;
902     
903     [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil];
904     _private->observingMouseMovedNotifications = false;
905 }
906
907 - (void)_removeSuperviewObservers
908 {
909     if (!_private || !_private->observingSuperviewNotifications)
910         return;
911     
912     NSView *superview = [self superview];
913     if (!superview || ![self window])
914         return;
915     
916     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
917     [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview];
918     [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview];
919     
920     _private->observingSuperviewNotifications = false;
921 }
922
923 - (void)_removeWindowObservers
924 {
925     if (!_private->observingWindowNotifications)
926         return;
927     
928     NSWindow *window = [self window];
929     if (!window)
930         return;
931     
932     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
933     [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
934     [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
935     [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window];
936     
937     _private->observingWindowNotifications = false;
938 }
939
940 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
941 {
942     WebView *webView = [self _webView];
943     DOMNode *child = [fragment firstChild];
944     if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
945         return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
946     return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
947 }
948
949 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
950 {
951     WebView *webView = [self _webView];
952     return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
953 }
954
955 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
956 {
957     return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
958 }
959
960 - (DOMRange *)_selectedRange
961 {
962     Frame* coreFrame = core([self _frame]);
963     return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil;
964 }
965
966 - (BOOL)_shouldDeleteRange:(DOMRange *)range
967 {
968     Frame* coreFrame = core([self _frame]);
969     return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range));
970 }
971
972 - (NSView *)_hitViewForEvent:(NSEvent *)event
973 {
974     // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.  
975     // Callers of this method, however, want to query the deepest view instead.
976     forceNSViewHitTest = YES;
977     NSView *hitView = [(NSView *)[[self window] contentView] hitTest:[event locationInWindow]];
978     forceNSViewHitTest = NO;    
979     return hitView;
980 }
981
982 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
983 {
984     // Put HTML on the pasteboard.
985     if ([types containsObject:WebArchivePboardType]) {
986         if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) {
987             if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0)
988                 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType];
989         }
990     }
991     
992     // Put the attributed string on the pasteboard (RTF/RTFD format).
993     if ([types containsObject:NSRTFDPboardType]) {
994         if (attributedString == nil) {
995             attributedString = [self selectedAttributedString];
996         }        
997         NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
998         [pasteboard setData:RTFDData forType:NSRTFDPboardType];
999     }        
1000     if ([types containsObject:NSRTFPboardType]) {
1001         if (!attributedString)
1002             attributedString = [self selectedAttributedString];
1003         if ([attributedString containsAttachments])
1004             attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
1005         NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
1006         [pasteboard setData:RTFData forType:NSRTFPboardType];
1007     }
1008     
1009     // Put plain string on the pasteboard.
1010     if ([types containsObject:NSStringPboardType]) {
1011         // Map &nbsp; to a plain old space because this is better for source code, other browsers do it,
1012         // and because HTML forces you to do this any time you want two spaces in a row.
1013         NSMutableString *s = [[self selectedString] mutableCopy];
1014         const unichar NonBreakingSpaceCharacter = 0xA0;
1015         NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
1016         [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
1017         [pasteboard setString:s forType:NSStringPboardType];
1018         [s release];
1019     }
1020     
1021     if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
1022         [pasteboard setData:nil forType:WebSmartPastePboardType];
1023     }
1024 }
1025
1026 - (void)_setMouseDownEvent:(NSEvent *)event
1027 {
1028     ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
1029
1030     if (event == _private->mouseDownEvent)
1031         return;
1032
1033     [event retain];
1034     [_private->mouseDownEvent release];
1035     _private->mouseDownEvent = event;
1036 }
1037
1038 - (void)_cancelUpdateMouseoverTimer
1039 {
1040     if (_private->updateMouseoverTimer) {
1041         CFRunLoopTimerInvalidate(_private->updateMouseoverTimer);
1042         CFRelease(_private->updateMouseoverTimer);
1043         _private->updateMouseoverTimer = NULL;
1044     }
1045 }
1046
1047 - (WebHTMLView *)_topHTMLView
1048 {
1049     // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
1050     WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
1051     ASSERT(!view || [view isKindOfClass:[WebHTMLView class]]);
1052     return view;
1053 }
1054
1055 - (BOOL)_isTopHTMLView
1056 {
1057     // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
1058     return self == [self _topHTMLView];
1059 }
1060
1061 - (void)_web_setPrintingModeRecursive
1062 {
1063     [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1064
1065 #ifndef NDEBUG
1066     _private->enumeratingSubviews = YES;
1067 #endif
1068
1069     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1070
1071     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1072
1073     unsigned count = [descendantWebHTMLViews count];
1074     for (unsigned i = 0; i < count; ++i)
1075         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1076
1077     [descendantWebHTMLViews release];
1078
1079 #ifndef NDEBUG
1080     _private->enumeratingSubviews = NO;
1081 #endif
1082 }
1083
1084 - (void)_web_clearPrintingModeRecursive
1085 {
1086     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1087
1088 #ifndef NDEBUG
1089     _private->enumeratingSubviews = YES;
1090 #endif
1091
1092     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1093
1094     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1095
1096     unsigned count = [descendantWebHTMLViews count];
1097     for (unsigned i = 0; i < count; ++i)
1098         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1099
1100     [descendantWebHTMLViews release];
1101
1102 #ifndef NDEBUG
1103     _private->enumeratingSubviews = NO;
1104 #endif
1105 }
1106
1107 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize
1108 {
1109     [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
1110
1111 #ifndef NDEBUG
1112     _private->enumeratingSubviews = YES;
1113 #endif
1114
1115     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1116
1117     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1118
1119     unsigned count = [descendantWebHTMLViews count];
1120     for (unsigned i = 0; i < count; ++i)
1121         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
1122
1123     [descendantWebHTMLViews release];
1124
1125 #ifndef NDEBUG
1126     _private->enumeratingSubviews = NO;
1127 #endif
1128 }
1129
1130 @end
1131
1132 @implementation WebHTMLView (WebPrivate)
1133
1134 + (NSArray *)supportedMIMETypes
1135 {
1136     return [WebHTMLRepresentation supportedMIMETypes];
1137 }
1138
1139 + (NSArray *)supportedImageMIMETypes
1140 {
1141     return [WebHTMLRepresentation supportedImageMIMETypes];
1142 }
1143
1144 + (NSArray *)supportedNonImageMIMETypes
1145 {
1146     return [WebHTMLRepresentation supportedNonImageMIMETypes];
1147 }
1148
1149 + (NSArray *)unsupportedTextMIMETypes
1150 {
1151     return [WebHTMLRepresentation unsupportedTextMIMETypes];
1152 }
1153
1154 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
1155 {
1156     // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged
1157     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1158         location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]]
1159         modifierFlags:[flagsChangedEvent modifierFlags]
1160         timestamp:[flagsChangedEvent timestamp]
1161         windowNumber:[flagsChangedEvent windowNumber]
1162         context:[flagsChangedEvent context]
1163         eventNumber:0 clickCount:0 pressure:0];
1164
1165     // Pretend it's a mouse move.
1166     [[NSNotificationCenter defaultCenter]
1167         postNotificationName:WKMouseMovedNotification() object:self
1168         userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]];
1169 }
1170
1171 - (id)_bridge
1172 {
1173     // This method exists to maintain compatibility with Leopard's Dictionary.app, since it
1174     // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:.
1175     // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160>
1176     return [self _frame];
1177 }
1178
1179 - (void)_updateMouseoverWithFakeEvent
1180 {
1181     [self _cancelUpdateMouseoverTimer];
1182     
1183     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1184         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1185         modifierFlags:[[NSApp currentEvent] modifierFlags]
1186         timestamp:[NSDate timeIntervalSinceReferenceDate]
1187         windowNumber:[[self window] windowNumber]
1188         context:[[NSApp currentEvent] context]
1189         eventNumber:0 clickCount:0 pressure:0];
1190     
1191     [self _updateMouseoverWithEvent:fakeEvent];
1192 }
1193
1194 static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info)
1195 {
1196     WebHTMLView *view = (WebHTMLView *)info;
1197     
1198     [view _updateMouseoverWithFakeEvent];
1199 }
1200
1201 - (void)_frameOrBoundsChanged
1202 {
1203     WebView *webView = [self _webView];
1204     WebDynamicScrollBarsView *scrollView = [[[webView mainFrame] frameView] _scrollView];
1205
1206     NSPoint origin = [[self superview] bounds].origin;
1207     if (!NSEqualPoints(_private->lastScrollPosition, origin) && ![scrollView inProgrammaticScroll]) {
1208         if (Frame* coreFrame = core([self _frame])) {
1209             if (FrameView* coreView = coreFrame->view()) {
1210                 _private->inScrollPositionChanged = YES;
1211                 coreView->scrollPositionChangedViaPlatformWidget();
1212                 _private->inScrollPositionChanged = NO;
1213             }
1214         }
1215     
1216         [_private->completionController endRevertingChange:NO moveLeft:NO];
1217         
1218         [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
1219     }
1220     _private->lastScrollPosition = origin;
1221
1222     if ([self window] && !_private->closed && !_private->updateMouseoverTimer) {
1223         CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL };
1224         
1225         // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading
1226         // and scrolling rapidly back to back.
1227         _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0,
1228                                                               _updateMouseoverTimerCallback, &context);
1229         CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode);
1230     }
1231     
1232 #if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD)
1233     [self _updateLayerHostingViewPosition];
1234 #endif
1235 }
1236
1237 - (void)_setAsideSubviews
1238 {
1239     ASSERT(!_private->subviewsSetAside);
1240     ASSERT(_private->savedSubviews == nil);
1241     _private->savedSubviews = _subviews;
1242 #if USE(ACCELERATED_COMPOSITING)
1243     // We need to keep the layer-hosting view in the subviews, otherwise the layers flash.
1244     if (_private->layerHostingView) {
1245         NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil];
1246         _subviews = newSubviews;
1247     } else
1248         _subviews = nil;
1249 #else
1250     _subviews = nil;
1251 #endif    
1252     _private->subviewsSetAside = YES;
1253  }
1254  
1255  - (void)_restoreSubviews
1256  {
1257     ASSERT(_private->subviewsSetAside);
1258 #if USE(ACCELERATED_COMPOSITING)
1259     if (_private->layerHostingView) {
1260         [_subviews release];
1261         _subviews = _private->savedSubviews;
1262     } else {
1263         ASSERT(_subviews == nil);
1264         _subviews = _private->savedSubviews;
1265     }
1266 #else
1267     ASSERT(_subviews == nil);
1268     _subviews = _private->savedSubviews;
1269 #endif    
1270     _private->savedSubviews = nil;
1271     _private->subviewsSetAside = NO;
1272 }
1273
1274 #ifndef NDEBUG
1275
1276 - (void)didAddSubview:(NSView *)subview
1277 {
1278     if (_private->enumeratingSubviews)
1279         LOG(View, "A view of class %s was added during subview enumeration for layout or printing mode change. This view might paint without first receiving layout.", object_getClassName([subview class]));
1280 }
1281 #endif
1282
1283
1284 - (void)viewWillDraw
1285 {
1286     // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1287     // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1288     // once the FIXME in _isTopHTMLView is fixed.
1289     if (_private->dataSource && [self _isTopHTMLView])
1290         [self _web_updateLayoutAndStyleIfNeededRecursive];
1291     [super viewWillDraw];
1292 }
1293
1294
1295 // Don't let AppKit even draw subviews. We take care of that.
1296 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1297 {
1298     // This helps when we print as part of a larger print process.
1299     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1300     BOOL wasInPrintingMode = _private->printing;
1301     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1302     if (isPrinting) {
1303         if (!wasInPrintingMode)
1304             [self _web_setPrintingModeRecursive];
1305         else
1306             [self _web_updateLayoutAndStyleIfNeededRecursive];
1307     } else if (wasInPrintingMode)
1308         [self _web_clearPrintingModeRecursive];
1309
1310     // There are known cases where -viewWillDraw is not called on all views being drawn.
1311     // See <rdar://problem/6964278> for example. Performing layout at this point prevents us from
1312     // trying to paint without layout (which WebCore now refuses to do, instead bailing out without
1313     // drawing at all), but we may still fail to update any regions dirtied by the layout which are
1314     // not already dirty. 
1315     if ([self _needsLayout]) {
1316         NSInteger rectCount;
1317         [self getRectsBeingDrawn:0 count:&rectCount];
1318         if (rectCount) {
1319             LOG_ERROR("View needs layout. Either -viewWillDraw wasn't called or layout was invalidated during the display operation. Performing layout now.");
1320             [self _web_updateLayoutAndStyleIfNeededRecursive];
1321         }
1322     }
1323
1324     [self _setAsideSubviews];
1325     [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1326     [self _restoreSubviews];
1327
1328     if (wasInPrintingMode != isPrinting) {
1329         if (wasInPrintingMode)
1330             [self _web_setPrintingModeRecursive];
1331         else
1332             [self _web_clearPrintingModeRecursive];
1333     }
1334 }
1335
1336 // Don't let AppKit even draw subviews. We take care of that.
1337 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1338 {
1339     BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1340
1341     BOOL wasInPrintingMode = _private->printing;
1342     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1343
1344     if (needToSetAsideSubviews) {
1345         // This helps when we print as part of a larger print process.
1346         // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1347         if (isPrinting) {
1348             if (!wasInPrintingMode)
1349                 [self _web_setPrintingModeRecursive];
1350             else
1351                 [self _web_updateLayoutAndStyleIfNeededRecursive];
1352         } else if (wasInPrintingMode)
1353             [self _web_clearPrintingModeRecursive];
1354
1355
1356         [self _setAsideSubviews];
1357     }
1358
1359     [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1360
1361     if (needToSetAsideSubviews) {
1362         if (wasInPrintingMode != isPrinting) {
1363             if (wasInPrintingMode)
1364                 [self _web_setPrintingModeRecursive];
1365             else
1366                 [self _web_clearPrintingModeRecursive];
1367         }
1368
1369         [self _restoreSubviews];
1370     }
1371 }
1372
1373 // Don't let AppKit even draw subviews. We take care of that.
1374 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView
1375 {
1376
1377     [self _setAsideSubviews];
1378     [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView];
1379     [self _restoreSubviews];
1380 }
1381
1382 - (BOOL)_insideAnotherHTMLView
1383 {
1384     return self != [self _topHTMLView];
1385 }
1386
1387 - (NSView *)hitTest:(NSPoint)point
1388 {
1389     // WebHTMLView objects handle all events for objects inside them.
1390     // To get those events, we prevent hit testing from AppKit.
1391
1392     // But there are three exceptions to this:
1393     //   1) For right mouse clicks and control clicks we don't yet have an implementation
1394     //      that works for nested views, so we let the hit testing go through the
1395     //      standard NSView code path (needs to be fixed, see bug 4361618).
1396     //   2) Java depends on doing a hit test inside it's mouse moved handling,
1397     //      so we let the hit testing go through the standard NSView code path
1398     //      when the current event is a mouse move (except when we are calling
1399     //      from _updateMouseoverWithEvent, so we have to use a global,
1400     //      forceWebHTMLViewHitTest, for that)
1401     //   3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1402     //      both need to figure out which view to check with inside the WebHTMLView.
1403     //      They use a global to change the behavior of hitTest: so they can get the
1404     //      right view. The global is forceNSViewHitTest and the method they use to
1405     //      do the hit testing is _hitViewForEvent:. (But this does not work correctly
1406     //      when there is HTML overlapping the view, see bug 4361626)
1407     //   4) NSAccessibilityHitTest relies on this for checking the cursor position.
1408     //      Our check for that is whether the event is NSFlagsChanged.  This works
1409     //      for VoiceOver's Control-Option-F5 command (move focus to item under cursor)
1410     //      and Dictionary's Command-Control-D (open dictionary popup for item under cursor).
1411     //      This is of course a hack.
1412
1413     if (_private->closed)
1414         return nil;
1415
1416     BOOL captureHitsOnSubviews;
1417     if (forceNSViewHitTest)
1418         captureHitsOnSubviews = NO;
1419     else if (forceWebHTMLViewHitTest)
1420         captureHitsOnSubviews = YES;
1421     else {
1422         // FIXME: Why doesn't this include mouse entered/exited events, or other mouse button events?
1423         NSEvent *event = [[self window] currentEvent];
1424         captureHitsOnSubviews = !([event type] == NSMouseMoved
1425             || [event type] == NSRightMouseDown
1426             || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1427             || [event type] == NSFlagsChanged);
1428     }
1429
1430     if (!captureHitsOnSubviews) {
1431         NSView* hitView = [super hitTest:point];
1432 #if USE(ACCELERATED_COMPOSITING)
1433         if (_private && hitView == _private->layerHostingView)
1434             hitView = self;
1435 #endif
1436         return hitView;
1437     }
1438     if ([[self superview] mouse:point inRect:[self frame]])
1439         return self;
1440     return nil;
1441 }
1442
1443 - (void)_clearLastHitViewIfSelf
1444 {
1445     if (lastHitView == self)
1446         lastHitView = nil;
1447 }
1448
1449 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1450 {
1451     ASSERT(_private->trackingRectOwner == nil);
1452     _private->trackingRectOwner = owner;
1453     _private->trackingRectUserData = data;
1454     return TRACKING_RECT_TAG;
1455 }
1456
1457 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1458 {
1459     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1460     ASSERT(_private->trackingRectOwner == nil);
1461     _private->trackingRectOwner = owner;
1462     _private->trackingRectUserData = data;
1463     return TRACKING_RECT_TAG;
1464 }
1465
1466 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1467 {
1468     ASSERT(count == 1);
1469     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1470     ASSERT(_private->trackingRectOwner == nil);
1471     _private->trackingRectOwner = owner;
1472     _private->trackingRectUserData = userDataList[0];
1473     trackingNums[0] = TRACKING_RECT_TAG;
1474 }
1475
1476 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1477 {
1478     if (tag == 0)
1479         return;
1480     
1481     if (_private && (tag == TRACKING_RECT_TAG)) {
1482         _private->trackingRectOwner = nil;
1483         return;
1484     }
1485     
1486     if (_private && (tag == _private->lastToolTipTag)) {
1487         [super removeTrackingRect:tag];
1488         _private->lastToolTipTag = 0;
1489         return;
1490     }
1491     
1492     // If any other tracking rect is being removed, we don't know how it was created
1493     // and it's possible there's a leak involved (see 3500217)
1494     ASSERT_NOT_REACHED();
1495 }
1496
1497 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1498 {
1499     int i;
1500     for (i = 0; i < count; ++i) {
1501         int tag = tags[i];
1502         if (tag == 0)
1503             continue;
1504         ASSERT(tag == TRACKING_RECT_TAG);
1505         if (_private != nil) {
1506             _private->trackingRectOwner = nil;
1507         }
1508     }
1509 }
1510
1511 - (void)_sendToolTipMouseExited
1512 {
1513     // Nothing matters except window, trackingNumber, and userData.
1514     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1515         location:NSMakePoint(0, 0)
1516         modifierFlags:0
1517         timestamp:0
1518         windowNumber:[[self window] windowNumber]
1519         context:NULL
1520         eventNumber:0
1521         trackingNumber:TRACKING_RECT_TAG
1522         userData:_private->trackingRectUserData];
1523     [_private->trackingRectOwner mouseExited:fakeEvent];
1524 }
1525
1526 - (void)_sendToolTipMouseEntered
1527 {
1528     // Nothing matters except window, trackingNumber, and userData.
1529     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1530         location:NSMakePoint(0, 0)
1531         modifierFlags:0
1532         timestamp:0
1533         windowNumber:[[self window] windowNumber]
1534         context:NULL
1535         eventNumber:0
1536         trackingNumber:TRACKING_RECT_TAG
1537         userData:_private->trackingRectUserData];
1538     [_private->trackingRectOwner mouseEntered:fakeEvent];
1539 }
1540
1541 - (void)_setToolTip:(NSString *)string
1542 {
1543     NSString *toolTip = [string length] == 0 ? nil : string;
1544     NSString *oldToolTip = _private->toolTip;
1545     if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1546         return;
1547     }
1548     if (oldToolTip) {
1549         [self _sendToolTipMouseExited];
1550         [oldToolTip release];
1551     }
1552     _private->toolTip = [toolTip copy];
1553     if (toolTip) {
1554         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1555         [self removeAllToolTips];
1556         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1557         _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1558         [self _sendToolTipMouseEntered];
1559     }
1560 }
1561
1562 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1563 {
1564     return [[_private->toolTip copy] autorelease];
1565 }
1566
1567 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1568 {
1569     if (_private->closed)
1570         return;
1571
1572     NSView *contentView = [[event window] contentView];
1573     NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1574     
1575     forceWebHTMLViewHitTest = YES;
1576     NSView *hitView = [contentView hitTest:locationForHitTest];
1577     forceWebHTMLViewHitTest = NO;
1578     
1579     WebHTMLView *view = nil;
1580     if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended])
1581         view = (WebHTMLView *)hitView;    
1582
1583     if (view)
1584         [view retain];
1585
1586     if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1587         // If we are moving out of a view (or frame), let's pretend the mouse moved
1588         // all the way out of that view. But we have to account for scrolling, because
1589         // WebCore doesn't understand our clipping.
1590         NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1591         float yScroll = visibleRect.origin.y;
1592         float xScroll = visibleRect.origin.x;
1593
1594         NSEvent *event = [NSEvent mouseEventWithType:NSMouseMoved
1595             location:NSMakePoint(-1 - xScroll, -1 - yScroll)
1596             modifierFlags:[[NSApp currentEvent] modifierFlags]
1597             timestamp:[NSDate timeIntervalSinceReferenceDate]
1598             windowNumber:[[view window] windowNumber]
1599             context:[[NSApp currentEvent] context]
1600             eventNumber:0 clickCount:0 pressure:0];
1601         if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1602             lastHitCoreFrame->eventHandler()->mouseMoved(event);
1603     }
1604
1605     lastHitView = view;
1606
1607     if (view) {
1608         if (Frame* coreFrame = core([view _frame]))
1609             coreFrame->eventHandler()->mouseMoved(event);
1610
1611         [view release];
1612     }
1613 }
1614
1615 + (NSArray *)_insertablePasteboardTypes
1616 {
1617     static NSArray *types = nil;
1618     if (!types) {
1619         types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType,
1620 #ifdef BUILDING_ON_LEOPARD
1621             NSPICTPboardType,
1622 #endif
1623             NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
1624         CFRetain(types);
1625     }
1626     return types;
1627 }
1628
1629 + (NSArray *)_selectionPasteboardTypes
1630 {
1631     // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1632     return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1633 }
1634
1635 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1636 {
1637     [self setPromisedDragTIFFDataSource:0];
1638 }
1639
1640 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1641 {
1642     if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1643         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1644         [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1645         [archive release];
1646     } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1647         if (Image* image = [self promisedDragTIFFDataSource]->image())
1648             [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1649         [self setPromisedDragTIFFDataSource:0];
1650     }
1651 }
1652
1653 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event 
1654
1655     [self autoscroll:event]; 
1656     [self _startAutoscrollTimer:event]; 
1657
1658
1659 - (WebPluginController *)_pluginController
1660 {
1661     return _private->pluginController;
1662 }
1663
1664 - (void)_layoutForPrinting
1665 {
1666     // Set printing mode temporarily so we can adjust the size of the view. This will allow
1667     // AppKit's pagination code to use the correct height for the page content. Leaving printing
1668     // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1669     // turn it off again after adjusting the size.
1670     [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1671     [self _web_clearPrintingModeRecursive];
1672 }
1673
1674 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1675 {
1676     if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
1677         if (beforeString)
1678             *beforeString = nil;
1679         if (afterString)
1680             *afterString = nil;
1681         return;
1682     }
1683     
1684     [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
1685 }
1686
1687 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
1688 {
1689     return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
1690 }
1691
1692 - (void)_startAutoscrollTimer:(NSEvent *)triggerEvent
1693 {
1694     if (_private->autoscrollTimer == nil) {
1695         _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1696             target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1697         _private->autoscrollTriggerEvent = [triggerEvent retain];
1698     }
1699 }
1700
1701 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1702 // We can't remove this yet because it's still in use by Mail.
1703 - (NSRect)_selectionRect
1704 {
1705     return [self selectionRect];
1706 }
1707
1708 - (void)_stopAutoscrollTimer
1709 {
1710     NSTimer *timer = _private->autoscrollTimer;
1711     _private->autoscrollTimer = nil;
1712     [_private->autoscrollTriggerEvent release];
1713     _private->autoscrollTriggerEvent = nil;
1714     [timer invalidate];
1715     [timer release];
1716 }
1717
1718 - (void)_autoscroll
1719 {
1720     // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1721     // a mouse up event.
1722     BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);   
1723     if (!isStillDown){
1724         [self _stopAutoscrollTimer];
1725         return;
1726     }
1727
1728     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1729         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1730         modifierFlags:[[NSApp currentEvent] modifierFlags]
1731         timestamp:[NSDate timeIntervalSinceReferenceDate]
1732         windowNumber:[[self window] windowNumber]
1733         context:[[NSApp currentEvent] context]
1734         eventNumber:0 clickCount:0 pressure:0];
1735     [self mouseDragged:fakeEvent];
1736 }
1737
1738 - (BOOL)_canEdit
1739 {
1740     Frame* coreFrame = core([self _frame]);
1741     return coreFrame && coreFrame->editor()->canEdit();
1742 }
1743
1744 - (BOOL)_canEditRichly
1745 {
1746     Frame* coreFrame = core([self _frame]);
1747     return coreFrame && coreFrame->editor()->canEditRichly();
1748 }
1749
1750 - (BOOL)_canAlterCurrentSelection
1751 {
1752     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1753 }
1754
1755 - (BOOL)_hasSelection
1756 {
1757     Frame* coreFrame = core([self _frame]);
1758     return coreFrame && coreFrame->selection()->isRange();
1759 }
1760
1761 - (BOOL)_hasSelectionOrInsertionPoint
1762 {
1763     Frame* coreFrame = core([self _frame]);
1764     return coreFrame && coreFrame->selection()->isCaretOrRange();
1765 }
1766
1767 - (BOOL)_hasInsertionPoint
1768 {
1769     Frame* coreFrame = core([self _frame]);
1770     return coreFrame && coreFrame->selection()->isCaret();
1771 }
1772
1773 - (BOOL)_isEditable
1774 {
1775     Frame* coreFrame = core([self _frame]);
1776     return coreFrame && coreFrame->selection()->isContentEditable();
1777 }
1778
1779 - (BOOL)_transparentBackground
1780 {
1781     return _private->transparentBackground;
1782 }
1783
1784 - (void)_setTransparentBackground:(BOOL)f
1785 {
1786     _private->transparentBackground = f;
1787 }
1788
1789 - (NSImage *)_selectionDraggingImage
1790 {
1791     if (![self _hasSelection])
1792         return nil;
1793     NSImage *dragImage = core([self _frame])->selectionImage();
1794     [dragImage _web_dissolveToFraction:WebDragImageAlpha];
1795     return dragImage;
1796 }
1797
1798 - (NSRect)_selectionDraggingRect
1799 {
1800     // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
1801     return [self selectionRect];
1802 }
1803
1804 - (DOMNode *)_insertOrderedList
1805 {
1806     Frame* coreFrame = core([self _frame]);
1807     return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil;
1808 }
1809
1810 - (DOMNode *)_insertUnorderedList
1811 {
1812     Frame* coreFrame = core([self _frame]);
1813     return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil;
1814 }
1815
1816 - (BOOL)_canIncreaseSelectionListLevel
1817 {
1818     Frame* coreFrame = core([self _frame]);
1819     return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel();
1820 }
1821
1822 - (BOOL)_canDecreaseSelectionListLevel
1823 {
1824     Frame* coreFrame = core([self _frame]);
1825     return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel();
1826 }
1827
1828 - (DOMNode *)_increaseSelectionListLevel
1829 {
1830     Frame* coreFrame = core([self _frame]);
1831     return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil;
1832 }
1833
1834 - (DOMNode *)_increaseSelectionListLevelOrdered
1835 {
1836     Frame* coreFrame = core([self _frame]);
1837     return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil;
1838 }
1839
1840 - (DOMNode *)_increaseSelectionListLevelUnordered
1841 {
1842     Frame* coreFrame = core([self _frame]);
1843     return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil;
1844 }
1845
1846 - (void)_decreaseSelectionListLevel
1847 {
1848     Frame* coreFrame = core([self _frame]);
1849     if (coreFrame)
1850         coreFrame->editor()->decreaseSelectionListLevel();
1851 }
1852
1853 - (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type
1854 {
1855     if (!_private->highlighters)
1856         _private->highlighters = [[NSMutableDictionary alloc] init];
1857     [_private->highlighters setObject:highlighter forKey:type];
1858 }
1859
1860 - (void)_removeHighlighterOfType:(NSString*)type
1861 {
1862     [_private->highlighters removeObjectForKey:type];
1863 }
1864
1865 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
1866 {
1867     ASSERT([self _hasSelection]);
1868     NSArray *types = [self pasteboardTypesForSelection];
1869
1870     // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
1871     NSAttributedString *attributedString = [self selectedAttributedString];
1872     NSMutableArray *mutableTypes = nil;
1873     if (![attributedString containsAttachments]) {
1874         mutableTypes = [types mutableCopy];
1875         [mutableTypes removeObject:NSRTFDPboardType];
1876         types = mutableTypes;
1877     }
1878
1879     [pasteboard declareTypes:types owner:[self _topHTMLView]];
1880     [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
1881     [mutableTypes release];
1882 }
1883
1884 - (void)close
1885 {
1886     // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing
1887     // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
1888     if (!_private || _private->closed)
1889         return;
1890
1891     _private->closed = YES;
1892
1893     [self _cancelUpdateMouseoverTimer];
1894     [self _clearLastHitViewIfSelf];
1895     [self _removeMouseMovedObserverUnconditionally];
1896     [self _removeWindowObservers];
1897     [self _removeSuperviewObservers];
1898     [_private->pluginController destroyAllPlugins];
1899     [_private->pluginController setDataSource:nil];
1900     // remove tooltips before clearing _private so removeTrackingRect: will work correctly
1901     [self removeAllToolTips];
1902
1903     if (_private->isInSecureInputState) {
1904         DisableSecureEventInput();
1905         _private->isInSecureInputState = NO;
1906     }
1907
1908     [_private clear];
1909 }
1910
1911 - (BOOL)_hasHTMLDocument
1912 {
1913     Frame* coreFrame = core([self _frame]);
1914     if (!coreFrame)
1915         return NO;
1916     Document* document = coreFrame->document();
1917     return document && document->isHTMLDocument();
1918 }
1919
1920 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
1921                                                  forType:(NSString *)pboardType
1922                                                inContext:(DOMRange *)context
1923                                             subresources:(NSArray **)subresources
1924 {
1925     if (pboardType == WebArchivePboardType) {
1926         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1927         if (subresources)
1928             *subresources = [archive subresources];
1929         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
1930         [archive release];
1931         return fragment;
1932     }
1933     if (pboardType == NSFilenamesPboardType)
1934         return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
1935         
1936     if (pboardType == NSHTMLPboardType) {
1937         NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
1938         // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
1939         if ([HTMLString hasPrefix:@"Version:"]) {
1940             NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
1941             if (range.location != NSNotFound)
1942                 HTMLString = [HTMLString substringFromIndex:range.location];
1943         }
1944         if ([HTMLString length] == 0)
1945             return nil;
1946         
1947         return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
1948     }
1949
1950     // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
1951     // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
1952     // FIXME: Remove this once bug 5052369 is fixed.
1953     if ([self _hasHTMLDocument] && (pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType)) {
1954         NSAttributedString *string = nil;
1955         if (pboardType == NSRTFDPboardType)
1956             string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
1957         if (string == nil)
1958             string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
1959         if (string == nil)
1960             return nil;
1961             
1962         NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
1963             [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
1964             self, @"WebResourceHandler", nil];
1965         NSArray *s;
1966         
1967         BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
1968         if (!wasDeferringCallbacks)
1969             [[self _webView] setDefersCallbacks:YES];
1970             
1971         DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length]) 
1972                                                           document:[[self _frame] DOMDocument] 
1973                                                 documentAttributes:documentAttributes
1974                                                       subresources:&s];
1975         if (subresources)
1976             *subresources = s;
1977         
1978         NSEnumerator *e = [s objectEnumerator];
1979         WebResource *r;
1980         while ((r = [e nextObject]))
1981             [[self _dataSource] addSubresource:r];
1982         
1983         if (!wasDeferringCallbacks)
1984             [[self _webView] setDefersCallbacks:NO];
1985         
1986         [documentAttributes release];
1987         [string release];
1988         return fragment;
1989     }
1990     if (pboardType == NSTIFFPboardType) {
1991         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
1992                                                               URL:uniqueURLWithRelativePart(@"image.tiff")
1993                                                          MIMEType:@"image/tiff" 
1994                                                  textEncodingName:nil
1995                                                         frameName:nil];
1996         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
1997         [resource release];
1998         return fragment;
1999     }
2000     if (pboardType == NSPDFPboardType) {
2001         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPDFPboardType]
2002                                                               URL:uniqueURLWithRelativePart(@"application.pdf")
2003                                                          MIMEType:@"application/pdf" 
2004                                                  textEncodingName:nil
2005                                                         frameName:nil];
2006         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2007         [resource release];
2008         return fragment;
2009     }
2010 #ifdef BUILDING_ON_LEOPARD
2011     if (pboardType == NSPICTPboardType) {
2012         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType]
2013                                                               URL:uniqueURLWithRelativePart(@"image.pict")
2014                                                          MIMEType:@"image/pict" 
2015                                                  textEncodingName:nil
2016                                                         frameName:nil];
2017         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2018         [resource release];
2019         return fragment;
2020     }
2021 #endif
2022     // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe
2023     // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard.
2024     if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) {
2025         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG]
2026                                                               URL:uniqueURLWithRelativePart(@"image.png")
2027                                                          MIMEType:@"image/png" 
2028                                                  textEncodingName:nil
2029                                                         frameName:nil];
2030         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2031         [resource release];
2032         return fragment;
2033     }
2034     if (pboardType == NSURLPboardType) {
2035         NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
2036         DOMDocument* document = [[self _frame] DOMDocument];
2037         ASSERT(document);
2038         if (!document)
2039             return nil;
2040         DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
2041         NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose.
2042         if ([URLString length] == 0)
2043             return nil;
2044         NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping];
2045         DOMText *text = [document createTextNode:URLTitleString];
2046         [anchor setHref:URLString];
2047         [anchor appendChild:text];
2048         DOMDocumentFragment *fragment = [document createDocumentFragment];
2049         [fragment appendChild:anchor];
2050         return fragment;
2051     }
2052     if (pboardType == NSStringPboardType)
2053         return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get());
2054     return nil;
2055 }
2056
2057 #if ENABLE(NETSCAPE_PLUGIN_API) 
2058 - (void)_pauseNullEventsForAllNetscapePlugins 
2059
2060     NSArray *subviews = [self subviews]; 
2061     unsigned int subviewCount = [subviews count]; 
2062     unsigned int subviewIndex; 
2063     
2064     for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { 
2065         NSView *subview = [subviews objectAtIndex:subviewIndex]; 
2066         if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) 
2067             [(WebBaseNetscapePluginView *)subview stopTimers];
2068     } 
2069
2070 #endif 
2071
2072 #if ENABLE(NETSCAPE_PLUGIN_API) 
2073 - (void)_resumeNullEventsForAllNetscapePlugins 
2074
2075     NSArray *subviews = [self subviews]; 
2076     unsigned int subviewCount = [subviews count]; 
2077     unsigned int subviewIndex; 
2078     
2079     for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { 
2080         NSView *subview = [subviews objectAtIndex:subviewIndex]; 
2081         if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) 
2082             [(WebBaseNetscapePluginView *)subview restartTimers]; 
2083     } 
2084
2085 #endif 
2086
2087 - (BOOL)_isUsingAcceleratedCompositing
2088 {
2089 #if USE(ACCELERATED_COMPOSITING)
2090     return _private->layerHostingView != nil;
2091 #else
2092     return NO;
2093 #endif
2094 }
2095
2096 - (NSView *)_compositingLayersHostingView
2097 {
2098 #if USE(ACCELERATED_COMPOSITING)
2099     return _private->layerHostingView;
2100 #else
2101     return 0;
2102 #endif
2103 }
2104
2105 - (BOOL)_isInPrintMode
2106 {
2107     return _private->printing;
2108 }
2109
2110 - (BOOL)_beginPrintModeWithMinimumPageWidth:(CGFloat)minimumPageWidth height:(CGFloat)minimumPageHeight maximumPageWidth:(CGFloat)maximumPageWidth
2111 {
2112     Frame* frame = core([self _frame]);
2113     if (!frame)
2114         return NO;
2115
2116     if (frame->document() && frame->document()->isFrameSet()) {
2117         minimumPageWidth = 0;
2118         minimumPageHeight = 0;
2119     }
2120
2121     float maximumShrinkRatio = 0;
2122     if (minimumPageWidth > 0.0)
2123         maximumShrinkRatio = maximumPageWidth / minimumPageWidth;
2124
2125     [self _setPrinting:YES minimumPageLogicalWidth:minimumPageWidth logicalHeight:minimumPageHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2126     return YES;
2127 }
2128
2129 - (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth height:(float)pageHeight shrinkToFit:(BOOL)shrinkToFit
2130 {
2131     Frame* frame = core([self _frame]);
2132     if (!frame)
2133         return NO;
2134
2135     Document* document = frame->document();
2136     bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
2137
2138     float pageLogicalWidth = isHorizontal ? pageWidth : pageHeight;
2139     float pageLogicalHeight = isHorizontal ? pageHeight : pageWidth;
2140     FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2141     float maximumShrinkRatio = 1;
2142
2143     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2144     // according to the page width.
2145     if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2146         minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2147         maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2148     }
2149
2150     [self _setPrinting:YES minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2151
2152     return YES;
2153 }
2154
2155 - (void)_endPrintMode
2156 {
2157     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2158 }
2159
2160 - (BOOL)_isInScreenPaginationMode
2161 {
2162     return _private->paginateScreenContent;
2163 }
2164
2165 - (BOOL)_beginScreenPaginationModeWithPageSize:(CGSize)pageSize shrinkToFit:(BOOL)shrinkToFit
2166 {
2167     Frame* frame = core([self _frame]);
2168     if (!frame)
2169         return NO;
2170
2171     Document* document = frame->document();
2172     bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode();
2173
2174     float pageLogicalWidth = isHorizontal ? pageSize.width : pageSize.height;
2175     float pageLogicalHeight = isHorizontal ? pageSize.height : pageSize.width;
2176     FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2177     float maximumShrinkRatio = 1;
2178
2179     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2180     // according to the page width.
2181     if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2182         minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2183         maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2184     }
2185
2186     [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2187
2188     return YES;
2189 }
2190
2191 - (void)_endScreenPaginationMode
2192 {
2193     [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:NO];
2194 }
2195
2196 - (CGFloat)_adjustedBottomOfPageWithTop:(CGFloat)top bottom:(CGFloat)bottom limit:(CGFloat)bottomLimit
2197 {
2198     Frame* frame = core([self _frame]);
2199     if (!frame)
2200         return bottom;
2201
2202     FrameView* view = frame->view();
2203     if (!view)
2204         return bottom;
2205
2206     float newBottom;
2207     view->adjustPageHeightDeprecated(&newBottom, top, bottom, bottomLimit);
2208
2209 #ifdef __LP64__
2210     // If the new bottom is equal to the old bottom (when both are treated as floats), we just return the original
2211     // bottom. This prevents rounding errors that can occur when converting newBottom to a double.
2212     if (fabs(static_cast<float>(bottom) - newBottom) <= numeric_limits<float>::epsilon()) 
2213         return bottom;
2214     else
2215 #endif
2216         return newBottom;
2217 }
2218
2219 @end
2220
2221 @implementation NSView (WebHTMLViewFileInternal)
2222
2223 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
2224 {
2225     unsigned count = [_subviews count];
2226     for (unsigned i = 0; i < count; ++i) {
2227         NSView *child = [_subviews objectAtIndex:i];
2228         if ([child isKindOfClass:[WebHTMLView class]])
2229             [array addObject:child];
2230         [child _web_addDescendantWebHTMLViewsToArray:array];
2231     }
2232 }
2233
2234 @end
2235
2236 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
2237
2238 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
2239 {
2240     if (object == nil) {
2241         [self removeObjectForKey:key];
2242     } else {
2243         [self setObject:object forKey:key];
2244     }
2245 }
2246
2247 @end
2248
2249 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
2250 {
2251     NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
2252     return [filename _webkit_hasCaseInsensitiveSuffix:extensionAsSuffix]
2253         || ([extension _webkit_isCaseInsensitiveEqualToString:@"jpeg"]
2254             && [filename _webkit_hasCaseInsensitiveSuffix:@".jpg"]);
2255 }
2256
2257
2258 @implementation WebHTMLView
2259
2260 + (void)initialize
2261 {
2262     [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] 
2263                              returnTypes:[[self class] _insertablePasteboardTypes]];
2264     JSC::initializeThreading();
2265     WTF::initializeMainThreadToProcessMainThread();
2266     WebCoreObjCFinalizeOnMainThread(self);
2267 }
2268
2269 - (id)initWithFrame:(NSRect)frame
2270 {
2271     self = [super initWithFrame:frame];
2272     if (!self)
2273         return nil;
2274     
2275     [self setFocusRingType:NSFocusRingTypeNone];
2276     
2277     // Make all drawing go through us instead of subviews.
2278     [self _setDrawsOwnDescendants:YES];
2279     
2280     _private = [[WebHTMLViewPrivate alloc] init];
2281
2282     _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2283     
2284     return self;
2285 }
2286
2287 - (void)dealloc
2288 {
2289     if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self))
2290         return;
2291
2292     // We can't assert that close has already been called because
2293     // this view can be removed from it's superview, even though
2294     // it could be needed later, so close if needed.
2295     [self close];
2296     [_private release];
2297     _private = nil;
2298     [super dealloc];
2299 }
2300
2301 - (void)finalize
2302 {
2303     ASSERT_MAIN_THREAD();
2304     // We can't assert that close has already been called because
2305     // this view can be removed from it's superview, even though
2306     // it could be needed later, so close if needed.
2307     [self close];
2308     [super finalize];
2309 }
2310
2311 // Returns YES if the delegate returns YES (so we should do no more work).
2312 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2313 {
2314     BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2315     _private->selectorForDoCommandBySelector = 0;
2316     if (callerAlreadyCalledDelegate)
2317         return NO;
2318     WebView *webView = [self _webView];
2319     return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2320 }
2321
2322 typedef HashMap<SEL, String> SelectorNameMap;
2323
2324 // Map selectors into Editor command names.
2325 // This is not needed for any selectors that have the same name as the Editor command.
2326 static const SelectorNameMap* createSelectorExceptionMap()
2327 {
2328     SelectorNameMap* map = new HashMap<SEL, String>;
2329
2330     map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline");
2331     map->add(@selector(insertParagraphSeparator:), "InsertNewline");
2332     map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab");
2333     map->add(@selector(pageDown:), "MovePageDown");
2334     map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection");
2335     map->add(@selector(pageUp:), "MovePageUp");
2336     map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection");
2337
2338     return map;
2339 }
2340
2341 static String commandNameForSelector(SEL selector)
2342 {
2343     // Check the exception map first.
2344     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
2345     SelectorNameMap::const_iterator it = exceptionMap->find(selector);
2346     if (it != exceptionMap->end())
2347         return it->second;
2348
2349     // Remove the trailing colon.
2350     // No need to capitalize the command name since Editor command names are
2351     // not case sensitive.
2352     const char* selectorName = sel_getName(selector);
2353     size_t selectorNameLength = strlen(selectorName);
2354     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2355         return String();
2356     return String(selectorName, selectorNameLength - 1);
2357 }
2358
2359 - (Editor::Command)coreCommandBySelector:(SEL)selector
2360 {
2361     Frame* coreFrame = core([self _frame]);
2362     if (!coreFrame)
2363         return Editor::Command();
2364     return coreFrame->editor()->command(commandNameForSelector(selector));
2365 }
2366
2367 - (Editor::Command)coreCommandByName:(const char*)name
2368 {
2369     Frame* coreFrame = core([self _frame]);
2370     if (!coreFrame)
2371         return Editor::Command();
2372     return coreFrame->editor()->command(name);
2373 }
2374
2375 - (void)executeCoreCommandBySelector:(SEL)selector
2376 {
2377     if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2378         return;
2379     [self coreCommandBySelector:selector].execute();
2380 }
2381
2382 - (void)executeCoreCommandByName:(const char*)name
2383 {
2384     [self coreCommandByName:name].execute();
2385 }
2386
2387 // These commands are forwarded to the Editor object in WebCore.
2388 // Ideally we'd do this for all editing commands; more of the code
2389 // should be moved from here to there, and more commands should be
2390 // added to this list.
2391
2392 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2393 // The functions are identical.
2394
2395 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2396
2397 WEBCORE_COMMAND(alignCenter)
2398 WEBCORE_COMMAND(alignJustified)
2399 WEBCORE_COMMAND(alignLeft)
2400 WEBCORE_COMMAND(alignRight)
2401 WEBCORE_COMMAND(copy)
2402 WEBCORE_COMMAND(cut)
2403 WEBCORE_COMMAND(paste)
2404 WEBCORE_COMMAND(delete)
2405 WEBCORE_COMMAND(deleteBackward)
2406 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2407 WEBCORE_COMMAND(deleteForward)
2408 WEBCORE_COMMAND(deleteToBeginningOfLine)
2409 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2410 WEBCORE_COMMAND(deleteToEndOfLine)
2411 WEBCORE_COMMAND(deleteToEndOfParagraph)
2412 WEBCORE_COMMAND(deleteToMark)
2413 WEBCORE_COMMAND(deleteWordBackward)
2414 WEBCORE_COMMAND(deleteWordForward)
2415 WEBCORE_COMMAND(ignoreSpelling)
2416 WEBCORE_COMMAND(indent)
2417 WEBCORE_COMMAND(insertBacktab)
2418 WEBCORE_COMMAND(insertLineBreak)
2419 WEBCORE_COMMAND(insertNewline)
2420 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2421 WEBCORE_COMMAND(insertParagraphSeparator)
2422 WEBCORE_COMMAND(insertTab)
2423 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2424 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
2425 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
2426 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
2427 WEBCORE_COMMAND(moveBackward)
2428 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2429 WEBCORE_COMMAND(moveDown)
2430 WEBCORE_COMMAND(moveDownAndModifySelection)
2431 WEBCORE_COMMAND(moveForward)
2432 WEBCORE_COMMAND(moveForwardAndModifySelection)
2433 WEBCORE_COMMAND(moveLeft)
2434 WEBCORE_COMMAND(moveLeftAndModifySelection)
2435 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2436 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2437 WEBCORE_COMMAND(moveRight)
2438 WEBCORE_COMMAND(moveRightAndModifySelection)
2439 WEBCORE_COMMAND(moveToBeginningOfDocument)
2440 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2441 WEBCORE_COMMAND(moveToBeginningOfLine)
2442 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2443 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2444 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2445 WEBCORE_COMMAND(moveToBeginningOfSentence)
2446 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2447 WEBCORE_COMMAND(moveToEndOfDocument)
2448 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2449 WEBCORE_COMMAND(moveToEndOfLine)
2450 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2451 WEBCORE_COMMAND(moveToEndOfParagraph)
2452 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2453 WEBCORE_COMMAND(moveToEndOfSentence)
2454 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2455 WEBCORE_COMMAND(moveToLeftEndOfLine)
2456 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
2457 WEBCORE_COMMAND(moveToRightEndOfLine)
2458 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
2459 WEBCORE_COMMAND(moveUp)
2460 WEBCORE_COMMAND(moveUpAndModifySelection)
2461 WEBCORE_COMMAND(moveWordBackward)
2462 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2463 WEBCORE_COMMAND(moveWordForward)
2464 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2465 WEBCORE_COMMAND(moveWordLeft)
2466 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2467 WEBCORE_COMMAND(moveWordRight)
2468 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2469 WEBCORE_COMMAND(outdent)
2470 WEBCORE_COMMAND(pageDown)
2471 WEBCORE_COMMAND(pageDownAndModifySelection)
2472 WEBCORE_COMMAND(pageUp)
2473 WEBCORE_COMMAND(pageUpAndModifySelection)
2474 WEBCORE_COMMAND(pasteAsPlainText)
2475 WEBCORE_COMMAND(selectAll)
2476 WEBCORE_COMMAND(selectLine)
2477 WEBCORE_COMMAND(selectParagraph)
2478 WEBCORE_COMMAND(selectSentence)
2479 WEBCORE_COMMAND(selectToMark)
2480 WEBCORE_COMMAND(selectWord)
2481 WEBCORE_COMMAND(setMark)
2482 WEBCORE_COMMAND(subscript)
2483 WEBCORE_COMMAND(superscript)
2484 WEBCORE_COMMAND(swapWithMark)
2485 WEBCORE_COMMAND(transpose)
2486 WEBCORE_COMMAND(underline)
2487 WEBCORE_COMMAND(unscript)
2488 WEBCORE_COMMAND(yank)
2489 WEBCORE_COMMAND(yankAndSelect)
2490
2491 #undef WEBCORE_COMMAND
2492
2493 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2494
2495 - (IBAction)takeFindStringFromSelection:(id)sender
2496 {
2497     COMMAND_PROLOGUE
2498
2499     if (![self _hasSelection]) {
2500         NSBeep();
2501         return;
2502     }
2503
2504     [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2505 }
2506
2507 // This method is needed to support Mac OS X services.
2508 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2509 {
2510     [pasteboard declareTypes:types owner:[self _topHTMLView]];
2511     [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2512     return YES;
2513 }
2514
2515 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2516 {
2517     BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]);
2518     BOOL isReturnTypeOK = NO;
2519     if (!returnType)
2520         isReturnTypeOK = YES;
2521     else if ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2522         // We can insert strings in any editable context.  We can insert other types, like images, only in rich edit contexts.
2523         isReturnTypeOK = [returnType isEqualToString:NSStringPboardType] || [self _canEditRichly];
2524     }
2525     if (isSendTypeOK && isReturnTypeOK)
2526         return self;
2527     return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2528 }
2529
2530 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2531 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2532 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2533 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2534 // might be using the selector, and we don't want to break them.
2535 - (void)jumpToSelection:(id)sender
2536 {
2537     COMMAND_PROLOGUE
2538
2539     if (Frame* coreFrame = core([self _frame]))
2540         coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways);
2541 }
2542
2543 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2544 {
2545     SEL action = [item action];
2546     RefPtr<Frame> frame = core([self _frame]);
2547
2548     if (!frame)
2549         return NO;
2550     
2551     if (Document* doc = frame->document()) {
2552         if (doc->isPluginDocument())
2553             return NO;
2554         if (doc->isImageDocument()) {            
2555             if (action == @selector(copy:))
2556                 return frame->loader()->isComplete();
2557             return NO;
2558         }
2559     }
2560
2561     if (action == @selector(changeSpelling:)
2562             || action == @selector(_changeSpellingFromMenu:)
2563             || action == @selector(checkSpelling:)
2564             || action == @selector(complete:)
2565             || action == @selector(pasteFont:))
2566         return [self _canEdit];
2567
2568     if (action == @selector(showGuessPanel:)) {
2569         // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2570         NSMenuItem *menuItem = (NSMenuItem *)item;
2571         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2572             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2573             [menuItem setTitle:panelShowing
2574                 ? UI_STRING_INTERNAL("Hide Spelling and Grammar", "menu item title")
2575                 : UI_STRING_INTERNAL("Show Spelling and Grammar", "menu item title")];
2576         }
2577         return [self _canEdit];
2578     }
2579     
2580     if (action == @selector(changeBaseWritingDirection:)
2581             || action == @selector(makeBaseWritingDirectionLeftToRight:)
2582             || action == @selector(makeBaseWritingDirectionRightToLeft:)) {
2583         NSWritingDirection writingDirection;
2584
2585         if (action == @selector(changeBaseWritingDirection:)) {
2586             writingDirection = static_cast<NSWritingDirection>([item tag]);
2587             if (writingDirection == NSWritingDirectionNatural)
2588                 return NO;
2589         } else if (action == @selector(makeBaseWritingDirectionLeftToRight:))
2590             writingDirection = NSWritingDirectionLeftToRight;
2591         else
2592             writingDirection = NSWritingDirectionRightToLeft;
2593
2594         NSMenuItem *menuItem = (NSMenuItem *)item;
2595         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2596             String direction = writingDirection == NSWritingDirectionLeftToRight ? "ltr" : "rtl";
2597             [menuItem setState:frame->editor()->selectionHasStyle(CSSPropertyDirection, direction)];
2598         }
2599         return [self _canEdit];
2600     }
2601
2602     if (action == @selector(makeBaseWritingDirectionNatural:)) {
2603         NSMenuItem *menuItem = (NSMenuItem *)item;
2604         if ([menuItem isKindOfClass:[NSMenuItem class]])
2605             [menuItem setState:NSOffState];
2606         return NO;
2607     }
2608
2609     if (action == @selector(toggleBaseWritingDirection:)) {
2610         NSMenuItem *menuItem = (NSMenuItem *)item;
2611         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2612             // Take control of the title of the menu item instead of just checking/unchecking it because
2613             // a check would be ambiguous.
2614             [menuItem setTitle:frame->editor()->selectionHasStyle(CSSPropertyDirection, "rtl")
2615                 ? UI_STRING_INTERNAL("Left to Right", "Left to Right context menu item")
2616                 : UI_STRING_INTERNAL("Right to Left", "Right to Left context menu item")];
2617         }
2618         return [self _canEdit];
2619     } 
2620     
2621     if (action == @selector(changeAttributes:)
2622             || action == @selector(changeColor:)        
2623             || action == @selector(changeFont:))
2624         return [self _canEditRichly];
2625     
2626     if (action == @selector(capitalizeWord:)
2627                || action == @selector(lowercaseWord:)
2628                || action == @selector(uppercaseWord:))
2629         return [self _hasSelection] && [self _isEditable];
2630
2631     if (action == @selector(centerSelectionInVisibleArea:)
2632                || action == @selector(jumpToSelection:)
2633                || action == @selector(copyFont:))
2634         return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2635     
2636     if (action == @selector(changeDocumentBackgroundColor:))
2637         return [[self _webView] isEditable] && [self _canEditRichly];
2638     
2639     if (action == @selector(_ignoreSpellingFromMenu:)
2640             || action == @selector(_learnSpellingFromMenu:)
2641             || action == @selector(takeFindStringFromSelection:))
2642         return [self _hasSelection];
2643     
2644     if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2645         return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste());
2646     
2647     if (action == @selector(pasteAsRichText:))
2648         return frame && (frame->editor()->canDHTMLPaste()
2649             || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable()));
2650     
2651     if (action == @selector(performFindPanelAction:))
2652         return NO;
2653     
2654     if (action == @selector(_lookUpInDictionaryFromMenu:))
2655         return [self _hasSelection];
2656
2657     if (action == @selector(stopSpeaking:))
2658         return [NSApp isSpeaking];
2659
2660     if (action == @selector(toggleGrammarChecking:)) {
2661         // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate 
2662         // the selector here because we implement it here, and we must implement it here because the AppKit 
2663         // code checks the first responder.
2664         NSMenuItem *menuItem = (NSMenuItem *)item;
2665         if ([menuItem isKindOfClass:[NSMenuItem class]])
2666             [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2667         return YES;
2668     }
2669
2670 #ifndef BUILDING_ON_LEOPARD
2671     if (action == @selector(orderFrontSubstitutionsPanel:)) {
2672         NSMenuItem *menuItem = (NSMenuItem *)item;
2673         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2674             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
2675             [menuItem setTitle:panelShowing
2676                 ? UI_STRING_INTERNAL("Hide Substitutions", "menu item title")
2677                 : UI_STRING_INTERNAL("Show Substitutions", "menu item title")];
2678         }
2679         return [self _canEdit];
2680     }
2681     // FIXME 4799134: WebView is the bottleneck for this logic, but we must validate 
2682     // the selector here because we implement it here, and we must implement it here because the AppKit 
2683     // code checks the first responder.
2684     if (action == @selector(toggleSmartInsertDelete:)) {
2685         NSMenuItem *menuItem = (NSMenuItem *)item;
2686         if ([menuItem isKindOfClass:[NSMenuItem class]])
2687             [menuItem setState:[self smartInsertDeleteEnabled] ? NSOnState : NSOffState];
2688         return [self _canEdit];
2689     }
2690     if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
2691         NSMenuItem *menuItem = (NSMenuItem *)item;
2692         if ([menuItem isKindOfClass:[NSMenuItem class]])
2693             [menuItem setState:[self isAutomaticQuoteSubstitutionEnabled] ? NSOnState : NSOffState];
2694         return [self _canEdit];
2695     }
2696     if (action == @selector(toggleAutomaticLinkDetection:)) {
2697         NSMenuItem *menuItem = (NSMenuItem *)item;
2698         if ([menuItem isKindOfClass:[NSMenuItem class]])
2699             [menuItem setState:[self isAutomaticLinkDetectionEnabled] ? NSOnState : NSOffState];
2700         return [self _canEdit];
2701     }
2702     if (action == @selector(toggleAutomaticDashSubstitution:)) {
2703         NSMenuItem *menuItem = (NSMenuItem *)item;
2704         if ([menuItem isKindOfClass:[NSMenuItem class]])
2705             [menuItem setState:[self isAutomaticDashSubstitutionEnabled] ? NSOnState : NSOffState];
2706         return [self _canEdit];
2707     }
2708     if (action == @selector(toggleAutomaticTextReplacement:)) {
2709         NSMenuItem *menuItem = (NSMenuItem *)item;
2710         if ([menuItem isKindOfClass:[NSMenuItem class]])
2711             [menuItem setState:[self isAutomaticTextReplacementEnabled] ? NSOnState : NSOffState];
2712         return [self _canEdit];
2713     }
2714     if (action == @selector(toggleAutomaticSpellingCorrection:)) {
2715         NSMenuItem *menuItem = (NSMenuItem *)item;
2716         if ([menuItem isKindOfClass:[NSMenuItem class]])
2717             [menuItem setState:[self isAutomaticSpellingCorrectionEnabled] ? NSOnState : NSOffState];
2718         return [self _canEdit];
2719     }
2720 #endif
2721     
2722     Editor::Command command = [self coreCommandBySelector:action];
2723     if (command.isSupported()) {
2724         NSMenuItem *menuItem = (NSMenuItem *)item;
2725         if ([menuItem isKindOfClass:[NSMenuItem class]])
2726             [menuItem setState:kit(command.state())];
2727         return command.isEnabled();
2728     }
2729
2730     return YES;
2731 }
2732
2733 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2734 {
2735     // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
2736     // assumes the WebVIew is non-nil.
2737     if (![self _webView])
2738         return NO;
2739     BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2740     return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2741 }
2742
2743 - (BOOL)acceptsFirstResponder
2744 {
2745     // Don't accept first responder when we first click on this view.
2746     // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2747     // Do accept first responder at any other time, for example from keyboard events,
2748     // or from calls back from WebCore once we begin mouse-down event handling.
2749     NSEvent *event = [NSApp currentEvent];
2750     if ([event type] == NSLeftMouseDown
2751             && !_private->handlingMouseDownEvent
2752             && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2753         return NO;
2754     }
2755     return YES;
2756 }
2757
2758 - (BOOL)maintainsInactiveSelection
2759 {
2760     // This method helps to determine whether the WebHTMLView should maintain
2761     // an inactive selection when it's not first responder.
2762     // Traditionally, these views have not maintained such selections,
2763     // clearing them when the view was not first responder. However,
2764     // to fix bugs like this one:
2765     // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even 
2766     //                            when they're not firstResponder"
2767     // it was decided to add a switch to act more like an NSTextView.
2768
2769     if ([[self _webView] maintainsInactiveSelection])
2770         return YES;
2771
2772     // Predict the case where we are losing first responder status only to
2773     // gain it back again. Want to keep the selection in that case.
2774     id nextResponder = [[self window] _newFirstResponderAfterResigning];
2775     if ([nextResponder isKindOfClass:[NSScrollView class]]) {
2776         id contentView = [nextResponder contentView];
2777         if (contentView)
2778             nextResponder = contentView;
2779     }
2780     if ([nextResponder isKindOfClass:[NSClipView class]]) {
2781         id documentView = [nextResponder documentView];
2782         if (documentView)
2783             nextResponder = documentView;
2784     }
2785     if (nextResponder == self)
2786         return YES;
2787
2788     Frame* coreFrame = core([self _frame]);
2789     bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable();
2790     bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
2791         && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
2792
2793     return selectionIsEditable && nextResponderIsInWebView;
2794 }
2795
2796 - (void)addMouseMovedObserver
2797 {
2798     if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications)
2799         return;
2800
2801     // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window.
2802     if (!([[self window] isKeyWindow] 
2803 #if ENABLE(DASHBOARD_SUPPORT)
2804             || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]
2805 #endif
2806         ))
2807         return;
2808
2809     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:)
2810         name:WKMouseMovedNotification() object:nil];
2811     [self _frameOrBoundsChanged];
2812     _private->observingMouseMovedNotifications = true;
2813 }
2814
2815 - (void)removeMouseMovedObserver
2816 {
2817 #if ENABLE(DASHBOARD_SUPPORT)
2818     // Don't remove the observer if we're running the Dashboard.
2819     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows])
2820         return;
2821 #endif
2822
2823     [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0];
2824     [self _removeMouseMovedObserverUnconditionally];
2825 }
2826
2827 - (void)addSuperviewObservers
2828 {
2829     if (_private->observingSuperviewNotifications)
2830         return;
2831
2832     NSView *superview = [self superview];
2833     if (!superview || ![self window])
2834         return;
2835     
2836     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2837     [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview];
2838     [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview];
2839     
2840     // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
2841     // It will check the current scroll against the previous layout's scroll.  We need to
2842     // do this here to catch the case where the WebView is laid out at one size, removed from its
2843     // window, resized, and inserted into another window.  Our frame/bounds changed notifications
2844     // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
2845     [self _frameOrBoundsChanged];
2846     
2847     _private->observingSuperviewNotifications = true;
2848 }
2849
2850 - (void)addWindowObservers
2851 {
2852     if (_private->observingWindowNotifications)
2853         return;
2854     
2855     NSWindow *window = [self window];
2856     if (!window)
2857         return;
2858     
2859     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
2860     [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
2861     [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
2862     [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
2863     
2864     _private->observingWindowNotifications = true;
2865 }
2866
2867 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
2868 {
2869     [self _removeSuperviewObservers];
2870 }
2871
2872 - (void)viewDidMoveToSuperview
2873 {
2874     if ([self superview] != nil)
2875         [self addSuperviewObservers];
2876
2877 #if USE(ACCELERATED_COMPOSITING)
2878     if ([self superview] && [self _isUsingAcceleratedCompositing]) {
2879         WebView *webView = [self _webView];
2880         if ([webView _postsAcceleratedCompositingNotifications])
2881             [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:webView userInfo:nil];
2882     }
2883 #endif
2884 }
2885
2886 - (void)viewWillMoveToWindow:(NSWindow *)window
2887 {
2888     // Don't do anything if we aren't initialized.  This happens
2889     // when decoding a WebView.  When WebViews are decoded their subviews
2890     // are created by initWithCoder: and so won't be normally
2891     // initialized.  The stub views are discarded by WebView.
2892     if (!_private)
2893         return;
2894
2895     // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
2896     [self _removeMouseMovedObserverUnconditionally];
2897     [self _removeWindowObservers];
2898     [self _removeSuperviewObservers];
2899     [self _cancelUpdateMouseoverTimer];
2900
2901     // FIXME: This accomplishes the same thing as the call to setCanStartMedia(false) in
2902     // WebView. It would be nice to have a single mechanism instead of two.
2903     [[self _pluginController] stopAllPlugins];
2904 }
2905
2906 - (void)viewDidMoveToWindow
2907 {
2908     // Don't do anything if we aren't initialized.  This happens
2909     // when decoding a WebView.  When WebViews are decoded their subviews
2910     // are created by initWithCoder: and so won't be normally
2911     // initialized.  The stub views are discarded by WebView.
2912     if (!_private || _private->closed)
2913         return;
2914         
2915     [self _stopAutoscrollTimer];
2916     if ([self window]) {
2917         _private->lastScrollPosition = [[self superview] bounds].origin;
2918         [self addWindowObservers];
2919         [self addSuperviewObservers];
2920         [self addMouseMovedObserver];
2921
2922         // FIXME: This accomplishes the same thing as the call to setCanStartMedia(true) in
2923         // WebView. It would be nice to have a single mechanism instead of two.
2924         [[self _pluginController] startAllPlugins];
2925
2926         _private->lastScrollPosition = NSZeroPoint;
2927     }
2928 }
2929
2930 - (void)_web_makePluginSubviewsPerformSelector:(SEL)selector withObject:(id)object
2931 {
2932 #if ENABLE(NETSCAPE_PLUGIN_API)
2933     // Copy subviews because [self subviews] returns the view's mutable internal array,
2934     // and we must avoid mutating the array while enumerating it.
2935     NSArray *subviews = [[self subviews] copy];
2936     
2937     NSEnumerator *enumerator = [subviews objectEnumerator];
2938     WebNetscapePluginView *view;
2939     while ((view = [enumerator nextObject]) != nil)
2940         if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
2941             [view performSelector:selector withObject:object];
2942     
2943     [subviews release];
2944 #endif
2945 }
2946
2947 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
2948 {
2949     [self _web_makePluginSubviewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
2950 }
2951
2952 - (void)viewDidMoveToHostWindow
2953 {
2954     [self _web_makePluginSubviewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
2955 }
2956
2957
2958 - (void)addSubview:(NSView *)view
2959 {
2960     [super addSubview:view];
2961
2962     if ([WebPluginController isPlugInView:view])
2963         [[self _pluginController] addPlugin:view];
2964 }
2965
2966 - (void)willRemoveSubview:(NSView *)subview
2967 {
2968 #ifndef NDEBUG
2969     // Have to null-check _private, since this can be called via -dealloc when
2970     // cleaning up the the layerHostingView.
2971     if (_private && _private->enumeratingSubviews)
2972         LOG(View, "A view of class %s was removed during subview enumeration for layout or printing mode change. We will still do layout or the printing mode change even though this view is no longer in the view hierarchy.", object_getClassName([subview class]));
2973 #endif
2974
2975     if ([WebPluginController isPlugInView:subview])
2976         [[self _pluginController] destroyPlugin:subview];
2977
2978     [super willRemoveSubview:subview];
2979 }
2980
2981 - (void)reapplyStyles
2982 {
2983 #ifdef LOG_TIMES
2984     double start = CFAbsoluteTimeGetCurrent();
2985 #endif
2986
2987     if (Frame* coreFrame = core([self _frame]))
2988         coreFrame->document()->styleSelectorChanged(RecalcStyleImmediately);
2989     
2990 #ifdef LOG_TIMES        
2991     double thisTime = CFAbsoluteTimeGetCurrent() - start;
2992     LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
2993 #endif
2994 }
2995
2996 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
2997 // minPageWidth==0 implies a non-printing layout
2998 - (void)layoutToMinimumPageWidth:(float)minPageLogicalWidth height:(float)minPageLogicalHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustingViewSize:(BOOL)adjustViewSize
2999 {    
3000     if (![self _needsLayout])
3001         return;
3002
3003 #ifdef LOG_TIMES        
3004     double start = CFAbsoluteTimeGetCurrent();
3005 #endif
3006
3007     LOG(View, "%@ doing layout", self);
3008
3009     Frame* coreFrame = core([self _frame]);
3010     if (!coreFrame)
3011         return;
3012
3013     if (FrameView* coreView = coreFrame->view()) {
3014         if (minPageLogicalWidth > 0.0) {
3015             FloatSize pageSize(minPageLogicalWidth, minPageLogicalHeight);
3016             if (coreFrame->document() && coreFrame->document()->renderView() && !coreFrame->document()->renderView()->style()->isHorizontalWritingMode())
3017                 pageSize = FloatSize(minPageLogicalHeight, minPageLogicalWidth);
3018             coreView->forceLayoutForPagination(pageSize, maximumShrinkRatio, adjustViewSize ? AdjustViewSize : DoNotAdjustViewSize);
3019         } else {
3020             coreView->forceLayout(!adjustViewSize);
3021             if (adjustViewSize)
3022                 coreView->adjustViewSize();
3023         }
3024     }
3025     
3026 #ifdef LOG_TIMES        
3027     double thisTime = CFAbsoluteTimeGetCurrent() - start;
3028     LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
3029 #endif
3030 }
3031
3032 - (void)layout
3033 {
3034     [self layoutToMinimumPageWidth:0 height:0 maximumShrinkRatio:0 adjustingViewSize:NO];
3035 }
3036
3037 // Deliver mouseup events to the DOM for button 2.
3038 - (void)rightMouseUp:(NSEvent *)event
3039 {
3040     // There's a chance that if we run a nested event loop the event will be released.
3041     // Retaining and then autoreleasing prevents that from causing a problem later here or
3042     // inside AppKit code.
3043     [[event retain] autorelease];
3044
3045     [super rightMouseUp:event];
3046
3047     if (Frame* coreframe = core([self _frame]))
3048         coreframe->eventHandler()->mouseUp(event);
3049 }
3050
3051 static void setMenuItemTarget(NSMenuItem* menuItem)
3052 {
3053     // Don't set the menu item's action to the context menu action forwarder if we already
3054     // have an action.
3055     if ([menuItem action])
3056         return;
3057
3058     [menuItem setTarget:[WebMenuTarget sharedMenuTarget]];
3059     [menuItem setAction:@selector(forwardContextMenuAction:)];
3060 }
3061
3062 static void setMenuTargets(NSMenu* menu)
3063 {
3064     NSInteger itemCount = [menu numberOfItems];
3065     for (NSInteger i = 0; i < itemCount; ++i) {
3066         NSMenuItem *item = [menu itemAtIndex:i];
3067         setMenuItemTarget(item);
3068         if ([item hasSubmenu])
3069             setMenuTargets([item submenu]);
3070     }
3071 }
3072
3073 - (NSMenu *)menuForEvent:(NSEvent *)event
3074 {
3075     // There's a chance that if we run a nested event loop the event will be released.
3076     // Retaining and then autoreleasing prevents that from causing a problem later here or
3077     // inside AppKit code.
3078     [[event retain] autorelease];
3079
3080     [_private->completionController endRevertingChange:NO moveLeft:NO];
3081
3082     RefPtr<Frame> coreFrame = core([self _frame]);
3083     if (!coreFrame)
3084         return nil;
3085
3086     Page* page = coreFrame->page();
3087     if (!page)
3088         return nil;
3089
3090     // Match behavior of other browsers by sending a mousedown event for right clicks.
3091     _private->handlingMouseDownEvent = YES;
3092     page->contextMenuController()->clearContextMenu();
3093     coreFrame->eventHandler()->mouseDown(event);
3094     BOOL handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(PlatformMouseEvent(event, page->chrome()->platformPageClient()));
3095     _private->handlingMouseDownEvent = NO;
3096
3097     if (!handledEvent)
3098         return nil;
3099
3100     // Re-get page, since it might have gone away during event handling.
3101     page = coreFrame->page();
3102     if (!page)
3103         return nil;
3104
3105     ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
3106     if (!coreMenu)
3107         return nil;
3108
3109     NSArray* menuItems = coreMenu->platformDescription();
3110     if (!menuItems)
3111         return nil;
3112
3113     NSUInteger count = [menuItems count];
3114     if (!count)
3115         return nil;
3116
3117     NSMenu* menu = [[[NSMenu alloc] init] autorelease];
3118     for (NSUInteger i = 0; i < count; i++)
3119         [menu addItem:[menuItems objectAtIndex:i]];
3120     setMenuTargets(menu);
3121     
3122     [[WebMenuTarget sharedMenuTarget] setMenuController:page->contextMenuController()];
3123     
3124     return menu;
3125 }
3126
3127 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
3128 {
3129     return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
3130 }
3131
3132 - (void)clearFocus
3133 {
3134     Frame* coreFrame = core([self _frame]);
3135     if (!coreFrame)
3136         return;
3137     Document* document = coreFrame->document();
3138     if (!document)
3139         return;
3140     
3141     document->setFocusedNode(0);
3142 }
3143
3144 - (BOOL)isOpaque
3145 {
3146     return [[self _webView] drawsBackground];
3147 }
3148
3149 #if !LOG_DISABLED
3150 - (void)setNeedsDisplay:(BOOL)flag
3151 {
3152     LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO");
3153     [super setNeedsDisplay:flag];
3154 }
3155 #endif
3156
3157 - (void)setNeedsDisplayInRect:(NSRect)invalidRect
3158 {
3159     if (_private->inScrollPositionChanged) {
3160         // When scrolling, the dirty regions are adjusted for the scroll only
3161         // after NSViewBoundsDidChangeNotification is sent. Translate the invalid
3162         // rect to pre-scrolled coordinates in order to get the right dirty region
3163         // after adjustment. See <rdar://problem/7678927>.
3164         NSPoint origin = [[self superview] bounds].origin;
3165         invalidRect.origin.x -= _private->lastScrollPosition.x - origin.x;
3166         invalidRect.origin.y -= _private->lastScrollPosition.y - origin.y;
3167     }
3168     [super setNeedsDisplayInRect:invalidRect];
3169 }
3170
3171 - (void)setNeedsLayout: (BOOL)flag
3172 {
3173     LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO");
3174     if (!flag)
3175         return; // There's no way to say you don't need a layout.
3176     if (Frame* frame = core([self _frame])) {
3177         if (frame->document() && frame->document()->inPageCache())
3178             return;
3179         if (FrameView* view = frame->view())
3180             view->setNeedsLayout();
3181     }
3182 }
3183
3184 - (void)setNeedsToApplyStyles: (BOOL)flag
3185 {
3186     LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO");
3187     if (!flag)
3188         return; // There's no way to say you don't need a style recalc.
3189     if (Frame* frame = core([self _frame])) {
3190         if (frame->document() && frame->document()->inPageCache())
3191             return;
3192         frame->document()->scheduleForcedStyleRecalc();
3193     }
3194 }
3195
3196 - (void)drawSingleRect:(NSRect)rect
3197 {
3198     [NSGraphicsContext saveGraphicsState];
3199     NSRectClip(rect);
3200         
3201     ASSERT([[self superview] isKindOfClass:[WebClipView class]]);
3202
3203     [(WebClipView *)[self superview] setAdditionalClip:rect];
3204
3205     @try {
3206         if ([self _transparentBackground]) {
3207             [[NSColor clearColor] set];
3208             NSRectFill (rect);
3209         }
3210
3211         [[self _frame] _drawRect:rect contentsOnly:YES];
3212
3213         WebView *webView = [self _webView];
3214
3215         // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be
3216         // called after the WebView has closed. If the client did not properly close the WebView and set the 
3217         // UIDelegate to nil, then the UIDelegate will be stale and this code will crash. 
3218         static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK);
3219         if (version3OrLaterClient)
3220             [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]];
3221
3222         if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight])
3223             [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]];
3224
3225         [(WebClipView *)[self superview] resetAdditionalClip];
3226
3227         [NSGraphicsContext restoreGraphicsState];
3228     } @catch (NSException *localException) {
3229         [(WebClipView *)[self superview] resetAdditionalClip];
3230         [NSGraphicsContext restoreGraphicsState];
3231         LOG_ERROR("Exception caught while drawing: %@", localException);
3232         [localException raise];
3233     }
3234 }
3235
3236 - (void)drawRect:(NSRect)rect
3237 {
3238     ASSERT_MAIN_THREAD();
3239     LOG(View, "%@ drawing", self);
3240
3241     const NSRect *rects;
3242     NSInteger count;
3243     [self getRectsBeingDrawn:&rects count:&count];
3244
3245     BOOL subviewsWereSetAside = _private->subviewsSetAside;
3246     if (subviewsWereSetAside)
3247         [self _restoreSubviews];
3248
3249 #ifdef LOG_TIMES
3250     double start = CFAbsoluteTimeGetCurrent();
3251 #endif
3252
3253     WebView *webView = [self _webView];
3254     if ([webView _mustDrawUnionedRect:rect singleRects:rects count:count])
3255         [self drawSingleRect:rect];
3256     else
3257         for (int i = 0; i < count; ++i)
3258             [self drawSingleRect:rects[i]];
3259
3260 #ifdef LOG_TIMES
3261     double thisTime = CFAbsoluteTimeGetCurrent() - start;
3262     LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime);
3263 #endif
3264
3265     if (subviewsWereSetAside)
3266         [self _setAsideSubviews];
3267
3268 #if USE(ACCELERATED_COMPOSITING)
3269     // Only do the synchronization dance if we're drawing into the window, otherwise
3270     // we risk disabling screen updates when no flush is pending.
3271     if ([NSGraphicsContext currentContext] == [[self window] graphicsContext] && [webView _needsOneShotDrawingSynchronization]) {
3272         // Disable screen updates to minimize the chances of the race between the CA
3273         // display link and AppKit drawing causing flashes.
3274         [[self window] disableScreenUpdatesUntilFlush];
3275         
3276         // Make sure any layer changes that happened as a result of layout
3277         // via -viewWillDraw are committed.
3278         [CATransaction flush];
3279         [webView _setNeedsOneShotDrawingSynchronization:NO];
3280     }
3281 #endif
3282
3283     if (webView)
3284         CallUIDelegate(webView, @selector(webView:didDrawFrame:), [self _frame]);
3285 }
3286
3287 // Turn off the additional clip while computing our visibleRect.
3288 - (NSRect)visibleRect
3289 {
3290     if (!([[self superview] isKindOfClass:[WebClipView class]]))
3291         return [super visibleRect];
3292
3293     WebClipView *clipView = (WebClipView *)[self superview];
3294
3295     BOOL hasAdditionalClip = [clipView hasAdditionalClip];
3296     if (!hasAdditionalClip) {
3297         return [super visibleRect];
3298     }
3299     
3300     NSRect additionalClip = [clipView additionalClip];
3301     [clipView resetAdditionalClip];
3302     NSRect visibleRect = [super visibleRect];
3303     [clipView setAdditionalClip:additionalClip];
3304     return visibleRect;
3305 }
3306
3307 - (void)_invalidateGStatesForTree
3308 {
3309     // AppKit is in the process of traversing the NSView tree, and is going to send -renewGState to
3310     // descendants, including plug-in views. This can result in calls out to plug-in code and back into
3311     // WebCore via JavaScript, which could normally mutate the NSView tree while it is being traversed.
3312     // Defer those mutations while descendants are being traveresed.
3313     RenderWidget::suspendWidgetHierarchyUpdates();
3314     [super _invalidateGStatesForTree];
3315     RenderWidget::resumeWidgetHierarchyUpdates();
3316 }
3317
3318 - (BOOL)isFlipped 
3319 {
3320     return YES;
3321 }
3322
3323 - (void)windowDidBecomeKey:(NSNotification *)notification
3324 {
3325     if (!pthread_main_np()) {
3326         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3327         return;
3328     }
3329
3330     NSWindow *keyWindow = [notification object];
3331
3332     if (keyWindow == [self window]) {
3333         [self addMouseMovedObserver];
3334         [self _updateSecureInputState];
3335     }
3336 }
3337
3338 - (void)windowDidResignKey:(NSNotification *)notification
3339 {
3340     if (!pthread_main_np()) {
3341         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3342         return;
3343     }
3344
3345     NSWindow *formerKeyWindow = [notification object];
3346
3347     if (formerKeyWindow == [self window])
3348         [self removeMouseMovedObserver];
3349
3350     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
3351         [self _updateSecureInputState];
3352         [_private->completionController endRevertingChange:NO moveLeft:NO];
3353     }
3354 }
3355
3356 - (void)windowWillClose:(NSNotification *)notification
3357 {
3358     if (!pthread_main_np()) {
3359         [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
3360         return;
3361     }
3362
3363     [_private->completionController endRevertingChange:NO moveLeft:NO];
3364     [[self _pluginController] destroyAllPlugins];
3365 }
3366
3367 - (void)scrollWheel:(NSEvent *)event
3368 {
3369     // There's a chance that responding to this event will run a nested event loop, and
3370     // fetching a new event might release the old one. Retaining and then autoreleasing
3371     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3372     [[event retain] autorelease];
3373
3374     Frame* frame = core([self _frame]);
3375     if (!frame || !frame->eventHandler()->wheelEvent(event))
3376         [super scrollWheel:event];
3377 }
3378
3379 - (BOOL)_isSelectionEvent:(NSEvent *)event
3380 {
3381     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3382     return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue];
3383 }
3384
3385 - (BOOL)_isScrollBarEvent:(NSEvent *)event
3386 {
3387     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
3388     return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsInScrollBarKey] boolValue];
3389 }
3390
3391 - (BOOL)acceptsFirstMouse:(NSEvent *)event
3392 {
3393     // There's a chance that responding to this event will run a nested event loop, and
3394     // fetching a new event might release the old one. Retaining and then autoreleasing
3395     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3396     [[event retain] autorelease];
3397
3398     NSView *hitView = [self _hitViewForEvent:event];
3399     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3400     
3401 #if ENABLE(DASHBOARD_SUPPORT)
3402     if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse])
3403         return YES;
3404 #endif
3405     
3406     if (hitHTMLView) {
3407         bool result = false;
3408         if (Frame* coreFrame = core([hitHTMLView _frame])) {
3409             coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]);
3410             [hitHTMLView _setMouseDownEvent:event];
3411             if ([hitHTMLView _isSelectionEvent:event]) {
3412                 if (Page* page = coreFrame->page())
3413                     result = coreFrame->eventHandler()->eventMayStartDrag(PlatformMouseEvent(event, page->chrome()->platformPageClient()));
3414             } else if ([hitHTMLView _isScrollBarEvent:event])
3415                 result = true;
3416             [hitHTMLView _setMouseDownEvent:nil];
3417         }
3418         return result;
3419     }
3420     return [hitView acceptsFirstMouse:event];
3421 }
3422
3423 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
3424 {
3425     // There's a chance that responding to this event will run a nested event loop, and
3426     // fetching a new event might release the old one. Retaining and then autoreleasing
3427     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3428     [[event retain] autorelease];
3429
3430     NSView *hitView = [self _hitViewForEvent:event];
3431     WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil;
3432     if (hitHTMLView) {
3433         bool result = false;
3434         if ([hitHTMLView _isSelectionEvent:event]) {
3435             if (Frame* coreFrame = core([hitHTMLView _frame])) {
3436                 [hitHTMLView _setMouseDownEvent:event];
3437                 if (Page* page = coreFrame->page())
3438                     result = coreFrame->eventHandler()->eventMayStartDrag(PlatformMouseEvent(event, page->chrome()->platformPageClient()));
3439                 [hitHTMLView _setMouseDownEvent:nil];
3440             }
3441         }
3442         return result;
3443     }
3444     return [hitView shouldDelayWindowOrderingForEvent:event];
3445 }
3446
3447 - (void)mouseDown:(NSEvent *)event
3448 {
3449     // There's a chance that responding to this event will run a nested event loop, and
3450     // fetching a new event might release the old one. Retaining and then autoreleasing
3451     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3452     [[event retain] autorelease];
3453
3454     RetainPtr<WebHTMLView> protector = self;
3455     if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event])
3456         return;
3457
3458     _private->handlingMouseDownEvent = YES;
3459
3460     // Record the mouse down position so we can determine drag hysteresis.
3461     [self _setMouseDownEvent:event];
3462
3463     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3464     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3465         goto done;
3466
3467     [_private->completionController endRevertingChange:NO moveLeft:NO];
3468
3469     // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here.
3470     // We don't want to pass them along to KHTML a second time.
3471     if (!([event modifierFlags] & NSControlKeyMask)) {
3472         _private->ignoringMouseDraggedEvents = NO;
3473
3474         // Don't do any mouseover while the mouse is down.
3475         [self _cancelUpdateMouseoverTimer];
3476
3477         // Let WebCore get a chance to deal with the event. This will call back to us
3478         // to start the autoscroll timer if appropriate.
3479         if (Frame* coreframe = core([self _frame]))
3480             coreframe->eventHandler()->mouseDown(event);
3481     }
3482
3483 done:
3484     _private->handlingMouseDownEvent = NO;
3485 }
3486
3487 - (void)dragImage:(NSImage *)dragImage
3488                at:(NSPoint)at
3489            offset:(NSSize)offset
3490             event:(NSEvent *)event
3491        pasteboard:(NSPasteboard *)pasteboard
3492            source:(id)source
3493         slideBack:(BOOL)slideBack
3494 {
3495     ASSERT(self == [self _topHTMLView]);
3496     [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack];
3497 }
3498
3499 - (void)mouseDragged:(NSEvent *)event
3500 {
3501     // There's a chance that responding to this event will run a nested event loop, and
3502     // fetching a new event might release the old one. Retaining and then autoreleasing
3503     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3504     [[event retain] autorelease];
3505
3506     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3507     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3508         return;
3509
3510     [self retain];
3511
3512     if (!_private->ignoringMouseDraggedEvents) {
3513         if (Frame* frame = core([self _frame])) {
3514             if (Page* page = frame->page())
3515                 page->mainFrame()->eventHandler()->mouseDragged(event);
3516         }
3517     }
3518
3519     [self release];
3520 }
3521
3522 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
3523 {
3524     ASSERT(![self _webView] || [self _isTopHTMLView]);
3525     
3526     Page* page = core([self _webView]);
3527     if (!page)
3528         return NSDragOperationNone;
3529
3530     return (NSDragOperation)page->dragController()->sourceDragOperation();
3531 }
3532
3533 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
3534 {
3535     ASSERT(![self _webView] || [self _isTopHTMLView]);
3536     
3537     NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
3538     NSPoint windowMouseLoc = windowImageLoc;
3539     
3540     if (Page* page = core([self _webView])) {
3541         DragController* dragController = page->dragController();
3542         windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y());
3543         dragController->dragEnded();
3544     }
3545     
3546     [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation];
3547     
3548     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3549     _private->ignoringMouseDraggedEvents = YES;
3550     
3551     // Once the dragging machinery kicks in, we no longer get mouse drags or the up event.
3552     // WebCore expects to get balanced down/up's, so we must fake up a mouseup.
3553     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
3554                                             location:windowMouseLoc
3555                                        modifierFlags:[[NSApp currentEvent] modifierFlags]
3556                                            timestamp:[NSDate timeIntervalSinceReferenceDate]
3557                                         windowNumber:[[self window] windowNumber]
3558                                              context:[[NSApp currentEvent] context]
3559                                          eventNumber:0 clickCount:0 pressure:0];
3560     [self mouseUp:fakeEvent]; // This will also update the mouseover state.
3561 }
3562
3563 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3564 {
3565     NSFileWrapper *wrapper = nil;
3566     NSURL *draggingImageURL = nil;
3567     
3568     if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) {
3569         
3570         SharedBuffer *buffer = static_cast<CachedResource*>(tiffResource)->data();
3571         if (!buffer)
3572             goto noPromisedData;
3573         
3574         NSData *data = buffer->createNSData();
3575         NSURLResponse *response = tiffResource->response().nsURLResponse();
3576         draggingImageURL = [response URL];
3577         wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
3578         NSString* filename = [response suggestedFilename];
3579         NSString* trueExtension(tiffResource->image()->filenameExtension());
3580         if (!matchesExtensionOrEquivalent(filename, trueExtension))
3581             filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension];
3582         [wrapper setPreferredFilename:filename];
3583     }
3584     
3585 noPromisedData:
3586     
3587     if (!wrapper) {
3588         ASSERT(![self _webView] || [self _isTopHTMLView]);
3589         Page* page = core([self _webView]);
3590         
3591         //If a load occurs midway through a drag, the view may be detached, which gives
3592         //us no ability to get to the original Page, so we cannot access any drag state
3593         //FIXME: is there a way to recover?
3594         if (!page) 
3595             return nil; 
3596         
3597         const KURL& imageURL = page->dragController()->draggingImageURL();
3598         ASSERT(!imageURL.isEmpty());
3599         draggingImageURL = imageURL;
3600
3601         wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL];
3602     }
3603     
3604     if (wrapper == nil) {
3605         LOG_ERROR("Failed to create image file.");
3606         return nil;
3607     }
3608
3609     // FIXME: Report an error if we fail to create a file.
3610     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3611     path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path];
3612     if (![wrapper writeToFile:path atomically:NO updateFilenames:YES])
3613         LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]");
3614     
3615     if (draggingImageURL)
3616         [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path];
3617     
3618     return [NSArray arrayWithObject:[path lastPathComponent]];
3619 }
3620
3621 - (void)mouseUp:(NSEvent *)event
3622 {
3623     // There's a chance that responding to this event will run a nested event loop, and
3624     // fetching a new event might release the old one. Retaining and then autoreleasing
3625     // the current event prevents that from causing a problem inside WebKit or AppKit code.
3626     [[event retain] autorelease];
3627
3628     [self _setMouseDownEvent:nil];
3629
3630     NSInputManager *currentInputManager = [NSInputManager currentInputManager];
3631     if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event])
3632         return;
3633
3634     [self retain];
3635
3636     [self _stopAutoscrollTimer];
3637     if (Frame* frame = core([self _frame])) {
3638         if (Page* page = frame->page())
3639             page->mainFrame()->eventHandler()->mouseUp(event);
3640     }
3641     [self _updateMouseoverWithFakeEvent];
3642
3643     [self release];
3644 }
3645
3646 - (void)mouseMovedNotification:(NSNotification *)notification
3647 {
3648     [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]];
3649 }
3650
3651 // returning YES from this method is the way we tell AppKit that it is ok for this view
3652 // to be in the key loop even when "tab to all controls" is not on.
3653 - (BOOL)needsPanelToBecomeKey
3654 {
3655     return YES;
3656 }
3657
3658 // Utility function to make sure we don't return anything through the NSTextInput
3659 // API when an editable region is not currently focused.
3660 static BOOL isTextInput(Frame* coreFrame)
3661 {
3662     return coreFrame && !coreFrame->selection()->isNone() && coreFrame->selection()->isContentEditable();
3663 }
3664
3665 static BOOL isInPasswordField(Frame* coreFrame)
3666 {
3667     return coreFrame && coreFrame->selection()->isInPasswordField();
3668 }
3669
3670 static PassRefPtr<KeyboardEvent> currentKeyboardEvent(Frame* coreFrame)
3671 {
3672     NSEvent *event = [NSApp currentEvent];
3673     if (!event)
3674         return 0;
3675
3676     switch ([event type]) {
3677     case NSKeyDown: {
3678         PlatformKeyboardEvent platformEvent(event);
3679         platformEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown);
3680         return KeyboardEvent::create(platformEvent, coreFrame->document()->defaultView());
3681     }
3682     case NSKeyUp:
3683         return KeyboardEvent::create(event, coreFrame->document()->defaultView());
3684     default:
3685         return 0;
3686     }
3687 }
3688
3689 - (BOOL)becomeFirstResponder
3690 {
3691     NSSelectionDirection direction = NSDirectSelection;
3692     if (![[self _webView] _isPerformingProgrammaticFocus])
3693         direction = [[self window] keyViewSelectionDirection];
3694
3695     [self _updateFontPanel];
3696     
3697     Frame* frame = core([self _frame]);
3698     if (!frame)
3699         return YES;
3700
3701     BOOL exposeInputContext = isTextInput(frame) && !isInPasswordField(frame);
3702     if (exposeInputContext != _private->exposeInputContext) {
3703         _private->exposeInputContext = exposeInputContext;
3704         [NSApp updateWindows];
3705     }
3706
3707     _private->_forceUpdateSecureInputState = YES;
3708     [self _updateSecureInputState];
3709     _private->_forceUpdateSecureInputState = NO;
3710
3711     frame->editor()->setStartNewKillRingSequence(true);
3712
3713     Page* page = frame->page();
3714     if (!page)
3715         return YES;
3716
3717     if (![[self _webView] _isPerformingProgrammaticFocus])
3718         page->focusController()->setFocusedFrame(frame);
3719
3720     page->focusController()->setFocused(true);
3721
3722     if (direction == NSDirectSelection)
3723         return YES;
3724
3725     if (Document* document = frame->document())
3726         document->setFocusedNode(0);
3727     page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward,
3728                                              currentKeyboardEvent(frame).get());
3729     return YES;
3730 }
3731
3732 - (BOOL)resignFirstResponder
3733 {
3734     BOOL resign = [super resignFirstResponder];
3735     if (resign) {
3736         if (_private->isInSecureInputState) {
3737             DisableSecureEventInput();
3738             _private->isInSecureInputState = NO;
3739         }
3740         [_private->completionController endRevertingChange:NO moveLeft:NO];
3741         Frame* coreFrame = core([self _frame]);
3742         if (!coreFrame)
3743             return resign;
3744         Page* page = coreFrame->page();
3745         if (!page)
3746             return resign;
3747         if (![self maintainsInactiveSelection]) { 
3748             [self deselectAll];
3749             if (![[self _webView] _isPerformingProgrammaticFocus])
3750                 [self clearFocus];
3751         }
3752         
3753         id nextResponder = [[self window] _newFirstResponderAfterResigning];
3754         bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
3755             && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
3756         if (!nextResponderIsInWebView)
3757             page->focusController()->setFocused(false);
3758     }
3759     return resign;
3760 }
3761
3762 - (void)setDataSource:(WebDataSource *)dataSource 
3763 {
3764     ASSERT(dataSource);
3765     if (_private->dataSource != dataSource) {
3766         ASSERT(!_private->closed);
3767         BOOL hadDataSource = _private->dataSource != nil;
3768
3769         [dataSource retain];
3770         [_private->dataSource release];
3771         _private->dataSource = dataSource;
3772         [_private->pluginController setDataSource:dataSource];
3773
3774         if (!hadDataSource)
3775             [self addMouseMovedObserver];
3776     }
3777 }
3778
3779 - (void)dataSourceUpdated:(WebDataSource *)dataSource
3780 {
3781 }
3782
3783 // This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes
3784 // key.  WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme
3785 // renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly).
3786 - (void)updateCell:(NSCell*)cell
3787 {
3788 }
3789
3790 // Does setNeedsDisplay:NO as a side effect when printing is ending.
3791 // pageWidth != 0 implies we will relayout to a new width
3792 - (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageLogicalWidth logicalHeight:(float)minPageLogicalHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent
3793 {
3794     if (printing == _private->printing && paginateScreenContent == _private->paginateScreenContent)
3795         return;
3796
3797     WebFrame *frame = [self _frame];
3798     NSArray *subframes = [frame childFrames];
3799     unsigned n = [subframes count];
3800     unsigned i;
3801     for (i = 0; i != n; ++i) {
3802         WebFrame *subframe = [subframes objectAtIndex:i];
3803         WebFrameView *frameView = [subframe frameView];
3804         if ([[subframe _dataSource] _isDocumentHTML]) {
3805             [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:adjustViewSize paginateScreenContent:paginateScreenContent];
3806         }
3807     }
3808
3809     [_private->pageRects release];
3810     _private->pageRects = nil;
3811     _private->printing = printing;
3812     _private->paginateScreenContent = paginateScreenContent;
3813     
3814     Frame* coreFrame = core([self _frame]);
3815     if (coreFrame) {
3816         if (FrameView* coreView = coreFrame->view())
3817             coreView->setMediaType(_private->printing ? "print" : "screen");
3818         if (Document* document = coreFrame->document()) {
3819             // In setting printing, we should not validate resources already cached for the document.
3820             // See https://bugs.webkit.org/show_bug.cgi?id=43704
3821             ResourceCacheValidationSuppressor validationSuppressor(document->cachedResourceLoader());
3822
3823             document->setPaginatedForScreen(_private->paginateScreenContent);
3824             document->setPrinting(_private->printing);
3825             document->styleSelectorChanged(RecalcStyleImmediately);
3826         }
3827     }
3828
3829     [self setNeedsLayout:YES];
3830     [self layoutToMinimumPageWidth:minPageLogicalWidth height:minPageLogicalHeight maximumShrinkRatio:maximumShrinkRatio adjustingViewSize:adjustViewSize];
3831     if (!printing) {
3832         // Can't do this when starting printing or nested printing won't work, see 3491427.
3833         [self setNeedsDisplay:NO];
3834     }
3835 }
3836
3837 - (BOOL)canPrintHeadersAndFooters
3838 {
3839     return YES;
3840 }
3841
3842 // This is needed for the case where the webview is embedded in the view that's being printed.
3843 // It shouldn't be called when the webview is being printed directly.
3844 - (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit
3845 {
3846     // This helps when we print as part of a larger print process.
3847     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
3848     BOOL wasInPrintingMode = _private->printing;
3849     if (!wasInPrintingMode)
3850         [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
3851
3852     *newBottom = [self _adjustedBottomOfPageWithTop:oldTop bottom:oldBottom limit:bottomLimit];
3853
3854     if (!wasInPrintingMode) {
3855         NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation];
3856         if (currenPrintOperation)
3857             // delay _setPrinting:NO until back to main loop as this method may get called repeatedly
3858             [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0];
3859         else
3860             // not sure if this is actually ever invoked, it probably shouldn't be
3861             [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
3862     }
3863 }
3864
3865 - (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation
3866 {
3867     bool useViewWidth = true;
3868     Frame* coreFrame = core([self _frame]);
3869     if (coreFrame) {
3870         Document* document = coreFrame->document();
3871         if (document && document->renderView())
3872             useViewWidth = document->renderView()->style()->isHorizontalWritingMode();
3873     }
3874
3875     float viewLogicalWidth = useViewWidth ? NSWidth([self bounds]) : NSHeight([self bounds]);
3876     if (viewLogicalWidth < 1) {
3877         LOG_ERROR("%@ has no logical width when printing", self);
3878         return 1.0f;
3879     }
3880
3881     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3882     float maxShrinkToFitScaleFactor = 1.0f / _WebHTMLViewPrintingMaximumShrinkFactor;
3883     float shrinkToFitScaleFactor = (useViewWidth ? [printOperation _web_availablePaperWidth] :  [printOperation _web_availablePaperHeight]) / viewLogicalWidth;
3884     return userScaleFactor * max(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor);
3885 }
3886
3887 // FIXME 3491344: This is a secret AppKit-internal method that we need to override in order
3888 // to get our shrink-to-fit to work with a custom pagination scheme. We can do this better
3889 // if AppKit makes it SPI/API.
3890 - (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation 
3891 {
3892     return [self _scaleFactorForPrintOperation:printOperation];
3893 }
3894
3895 // This is used for Carbon printing. At some point we might want to make this public API.
3896 - (void)setPageWidthForPrinting:(float)pageWidth
3897 {
3898     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
3899     [self _setPrinting:YES minimumPageLogicalWidth:pageWidth logicalHeight:0 maximumShrinkRatio:1 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
3900 }
3901
3902 - (void)_endPrintModeAndRestoreWindowAutodisplay
3903 {
3904     [self _endPrintMode];
3905     [[self window] setAutodisplay:YES];
3906 }
3907
3908 - (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation
3909 {
3910     ASSERT_ARG(initiatingOperation, initiatingOperation != nil);
3911     NSPrintOperation *currentOperation = [NSPrintOperation currentOperation];
3912     if (initiatingOperation == currentOperation) {
3913         // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're
3914         // being extra paranoid here since the printing code is so fragile. Delay the cleanup
3915         // further.
3916         ASSERT_NOT_REACHED();
3917         [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0];
3918     } else if ([currentOperation view] == self) {
3919         // A new print job has started, but it is printing the same WebHTMLView again. We don't expect
3920         // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so
3921         // fragile. Do nothing, because we don't want to break the print job currently in progress, and
3922         // the print job currently in progress is responsible for its own cleanup.
3923         ASSERT_NOT_REACHED();
3924     } else {
3925         // The print job that kicked off this delayed call has finished, and this view is not being
3926         // printed again. We expect that no other print job has started. Since this delayed call wasn't
3927         // cancelled, beginDocument and endDocument must not have been called, and we need to clean up
3928         // the print mode here.
3929         ASSERT(currentOperation == nil);
3930         [self _endPrintModeAndRestoreWindowAutodisplay];
3931     }
3932 }
3933
3934 // Return the number of pages available for printing
3935 - (BOOL)knowsPageRange:(NSRangePointer)range
3936 {
3937     // Must do this explicit display here, because otherwise the view might redisplay while the print
3938     // sheet was up, using printer fonts (and looking different).
3939     [self displayIfNeeded];
3940     [[self window] setAutodisplay:NO];    
3941
3942     [[self _webView] _adjustPrintingMarginsForHeaderAndFooter];
3943     NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
3944     if (![self _beginPrintModeWithPageWidth:[printOperation _web_availablePaperWidth] height:[printOperation _web_availablePaperHeight] shrinkToFit:YES])
3945         return NO;
3946
3947     // Certain types of errors, including invalid page ranges, can cause beginDocument and
3948     // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases
3949     // we need to get out of print mode without relying on any more callbacks from the printing mechanism.
3950     // If we get as far as beginDocument without trouble, then this delayed request will be cancelled.
3951     // If not cancelled, this delayed call will be invoked in the next pass through the main event loop,
3952     // which is after beginDocument and endDocument would be called.
3953     [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0];
3954     
3955     // There is a theoretical chance that someone could do some drawing between here and endDocument,
3956     // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because
3957     // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari.
3958
3959     range->location = 1;
3960     float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation];
3961     float userScaleFactor = [printOperation _web_pageSetupScaleFactor];
3962     [_private->pageRects release];
3963     float fullPageWidth = floorf([printOperation _web_availablePaperWidth] / totalScaleFactor);
3964     float fullPageHeight = floorf([printOperation _web_availablePaperHeight] / totalScaleFactor);
3965     WebFrame *frame = [self _frame];
3966     NSArray *newPageRects = [frame _computePageRectsWithPrintScaleFactor:userScaleFactor pageSize:NSMakeSize(fullPageWidth, fullPageHeight)];
3967     
3968     // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we
3969     // hit that case we'll pass along a degenerate 1 pixel square to print. This will print
3970     // a blank page (with correct-looking header and footer if that option is on), which matches
3971     // the behavior of IE and Camino at least.
3972     if ([newPageRects count] == 0)
3973         newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]];
3974     
3975     _private->pageRects = [newPageRects retain];
3976     
3977     range->length = [_private->pageRects count];
3978     
3979     return YES;
3980 }
3981
3982 // Return the drawing rectangle for a particular page number
3983 - (NSRect)rectForPage:(NSInteger)page
3984 {
3985     return [[_private->pageRects objectAtIndex:page - 1] rectValue];
3986 }
3987
3988 - (void)drawPageBorderWithSize:(NSSize)borderSize
3989 {
3990     ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize]));    
3991     [[self _webView] _drawHeaderAndFooter];
3992 }
3993
3994 - (void)beginDocument
3995 {
3996     @try {
3997         // From now on we'll get a chance to call _endPrintMode in either beginDocument or
3998         // endDocument, so we can cancel the "just in case" pending call.
3999         [NSObject cancelPreviousPerformRequestsWithTarget:self
4000                                                  selector:@selector(_delayedEndPrintMode:)
4001                                                    object:[NSPrintOperation currentOperation]];
4002         [super beginDocument];
4003     } @catch (NSException *localException) {
4004         // Exception during [super beginDocument] means that endDocument will not get called,
4005         // so we need to clean up our "print mode" here.
4006         [self _endPrintModeAndRestoreWindowAutodisplay];
4007     }
4008 }
4009
4010 - (void)endDocument
4011 {
4012     [super endDocument];
4013     // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO 
4014     [self _endPrintModeAndRestoreWindowAutodisplay];
4015 }
4016
4017 - (void)keyDown:(NSEvent *)event
4018 {
4019     // There's a chance that responding to this event will run a nested event loop, and
4020     // fetching a new event might release the old one. Retaining and then autoreleasing
4021     // the current event prevents that from causing a problem inside WebKit or AppKit code.
4022     [[event retain] autorelease];
4023
4024     RetainPtr<WebHTMLView> selfProtector = self;
4025     BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
4026
4027     BOOL callSuper = NO;
4028
4029     [_private->keyDownEvent release];
4030     _private->keyDownEvent = [event retain];
4031
4032     BOOL completionPopupWasOpen = _private->completionController && [_private->completionController popupWindowIsOpen];
4033     Frame* coreFrame = core([self _frame]);
4034     if (!eventWasSentToWebCore && coreFrame && coreFrame->eventHandler()->keyEvent(event)) {
4035         // WebCore processed a key event, bail on any preexisting complete: UI
4036         if (completionPopupWasOpen)
4037             [_private->completionController endRevertingChange:YES moveLeft:NO];
4038     } else if (!_private->completionController || ![_private->completionController filterKeyDown:event]) {
4039         // Not consumed by complete: popup window
4040         [_private->completionController endRevertingChange:YES moveLeft:NO];
4041         callSuper = YES;
4042     }
4043     if (callSuper)
4044         [super keyDown:event];
4045     else
4046         [NSCursor setHiddenUntilMouseMoves:YES];
4047 }
4048
4049 - (void)keyUp:(NSEvent *)event
4050 {
4051     // There's a chance that responding to this event will run a nested event loop, and
4052     // fetching a new event might release the old one. Retaining and then autoreleasing
4053     // the current event prevents that from causing a problem inside WebKit or AppKit code.
4054     [[event retain] autorelease];
4055
4056     BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
4057
4058     RetainPtr<WebHTMLView> selfProtector = self;
4059     Frame* coreFrame = core([self _frame]);
4060     if (coreFrame && !eventWasSentToWebCore)
4061         coreFrame->eventHandler()->keyEvent(event);
4062     else
4063         [super keyUp:event];
4064 }
4065
4066 - (void)flagsChanged:(NSEvent *)event
4067 {
4068     // There's a chance that responding to this event will run a nested event loop, and
4069     // fetching a new event might release the old one. Retaining and then autoreleasing
4070     // the current event prevents that from causing a problem inside WebKit or AppKit code.
4071     [[event retain] autorelease];
4072
4073     RetainPtr<WebHTMLView> selfProtector = self;
4074
4075     Frame* coreFrame = core([self _frame]);
4076     unsigned short keyCode = [event keyCode];
4077
4078     // Don't make an event from the num lock and function keys.
4079     if (coreFrame && keyCode != 0 && keyCode != 10 && keyCode != 63) {
4080         coreFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(event));
4081         return;
4082     }
4083         
4084     [super flagsChanged:event];
4085 }
4086
4087 - (id)accessibilityAttributeValue:(NSString*)attributeName
4088 {
4089     if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
4090         id accTree = [[self _frame] accessibilityRoot];
4091         if (accTree)
4092             return [NSArray arrayWithObject:accTree];
4093         return nil;
4094     }
4095     return [super accessibilityAttributeValue:attributeName];
4096 }
4097
4098 - (id)accessibilityFocusedUIElement
4099 {
4100     id accTree = [[self _frame] accessibilityRoot];
4101     if (accTree)
4102         return [accTree accessibilityFocusedUIElement];
4103     return self;
4104 }
4105
4106 - (id)accessibilityHitTest:(NSPoint)point
4107 {
4108     id accTree = [[self _frame] accessibilityRoot];
4109     if (accTree) {
4110         NSPoint windowCoord = [[self window] convertScreenToBase:point];
4111         return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]];
4112     }
4113     return self;
4114 }
4115
4116 - (id)_accessibilityParentForSubview:(NSView *)subview
4117 {
4118     id accTree = [[self _frame] accessibilityRoot];
4119     if (!accTree)
4120         return self;
4121     id parent = [accTree _accessibilityParentForSubview:subview];
4122     if (!parent)
4123         return self;
4124     return parent;
4125 }
4126
4127 - (void)centerSelectionInVisibleArea:(id)sender
4128 {
4129     COMMAND_PROLOGUE
4130
4131     if (Frame* coreFrame = core([self _frame]))
4132         coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways);
4133 }
4134
4135 - (NSData *)_selectionStartFontAttributesAsRTF
4136 {
4137     Frame* coreFrame = core([self _frame]);
4138     NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x"
4139         attributes:coreFrame ? coreFrame->editor()->fontAttributesForSelectionStart() : nil];
4140     NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
4141     [string release];
4142     return data;
4143 }
4144
4145 - (NSDictionary *)_fontAttributesFromFontPasteboard
4146 {
4147     NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
4148     if (fontPasteboard == nil)
4149         return nil;
4150     NSData *data = [fontPasteboard dataForType:NSFontPboardType];
4151     if (data == nil || [data length] == 0)
4152         return nil;
4153     // NSTextView does something more efficient by parsing the attributes only, but that's not available in API.
4154     NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease];
4155     if (string == nil || [string length] == 0)
4156         return nil;
4157     return [string fontAttributesInRange:NSMakeRange(0, 1)];
4158 }
4159
4160 - (DOMCSSStyleDeclaration *)_emptyStyle
4161 {
4162     return [[[self _frame] DOMDocument] createCSSStyleDeclaration];
4163 }
4164
4165 - (NSString *)_colorAsString:(NSColor *)color
4166 {
4167     NSColor *rgbColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
4168     // FIXME: If color is non-nil and rgbColor is nil, that means we got some kind
4169     // of fancy color that can't be converted to RGB. Changing that to "transparent"
4170     // might not be great, but it's probably OK.
4171     if (rgbColor == nil)
4172         return @"transparent";
4173     float r = [rgbColor redComponent];
4174     float g = [rgbColor greenComponent];
4175     float b = [rgbColor blueComponent];
4176     float a = [rgbColor alphaComponent];
4177     if (a == 0)
4178         return @"transparent";
4179     if (r == 0 && g == 0 && b == 0 && a == 1)
4180         return @"black";
4181     if (r == 1 && g == 1 && b == 1 && a == 1)
4182         return @"white";
4183     // FIXME: Lots more named colors. Maybe we could use the table in WebCore?
4184     if (a == 1)
4185         return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255];
4186     return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a];
4187 }
4188
4189 - (NSString *)_shadowAsString:(NSShadow *)shadow
4190 {
4191     if (shadow == nil)
4192         return @"none";
4193     NSSize offset = [shadow shadowOffset];
4194     float blurRadius = [shadow shadowBlurRadius];
4195     if (offset.width == 0 && offset.height == 0 && blurRadius == 0)
4196         return @"none";
4197     NSColor *color = [shadow shadowColor];
4198     if (color == nil)
4199         return @"none";
4200     // FIXME: Handle non-integral values here?
4201     if (blurRadius == 0)
4202         return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height];
4203     return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius];
4204 }
4205
4206 - (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary
4207 {
4208     DOMCSSStyleDeclaration *style = [self _emptyStyle];
4209
4210     NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName];
4211     [style setBackgroundColor:[self _colorAsString:color]];
4212
4213     NSFont *font = [dictionary objectForKey:NSFontAttributeName];
4214     if (!font) {
4215         [style setFontFamily:@"Helvetica"];
4216         [style setFontSize:@"12px"];
4217         [style setFontWeight:@"normal"];
4218         [style setFontStyle:@"normal"];
4219     } else {
4220         NSFontManager *fm = [NSFontManager sharedFontManager];
4221         // FIXME: Need more sophisticated escaping code if we want to handle family names
4222         // with characters like single quote or backslash in their names.
4223         [style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]];
4224         [style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]];
4225         // FIXME: Map to the entire range of CSS weight values.
4226         if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT)
4227             [style setFontWeight:@"bold"];
4228         else
4229             [style setFontWeight:@"normal"];
4230         if ([fm traitsOfFont:font] & NSItalicFontMask)
4231             [style setFontStyle:@"italic"];
4232         else
4233             [style setFontStyle:@"normal"];
4234     }
4235
4236     color = [dictionary objectForKey:NSForegroundColorAttributeName];
4237     [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"];
4238
4239     NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName];
4240     [style setTextShadow:[self _shadowAsString:shadow]];
4241
4242     int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue];
4243
4244     int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue];
4245     if (superscriptInt > 0)
4246         [style setVerticalAlign:@"super"];
4247     else if (superscriptInt < 0)
4248         [style setVerticalAlign:@"sub"];
4249     else
4250         [style setVerticalAlign:@"baseline"];
4251     int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue];
4252     // FIXME: Underline wins here if we have both (see bug 3790443).
4253     if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone)
4254         [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
4255     else if (underlineInt == NSUnderlineStyleNone)
4256         [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
4257     else
4258         [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
4259
4260     return style;
4261 }
4262
4263 - (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
4264 {
4265     if (Frame* coreFrame = core([self _frame]))
4266         coreFrame->editor()->applyStyleToSelection(core(style), undoAction);
4267 }
4268
4269 - (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
4270 {
4271     if (Frame* coreFrame = core([self _frame]))
4272         coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction);
4273 }
4274
4275 - (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event
4276 {
4277     WebView *webView = [self _webView];
4278     if (!webView)
4279         return NO;
4280
4281     if (![[webView preferences] respectStandardStyleKeyEquivalents])
4282         return NO;
4283     
4284     if (![self _canEdit])
4285         return NO;
4286     
4287     if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask)
4288         return NO;
4289     
4290     NSString *string = [event characters];
4291     if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) {
4292         [self executeCoreCommandByName:"ToggleBold"];
4293         return YES;
4294     }
4295     if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) {
4296         [self executeCoreCommandByName:"ToggleItalic"];
4297         return YES;
4298     }
4299     
4300     return NO;
4301 }
4302
4303 - (BOOL)performKeyEquivalent:(NSEvent *)event
4304 {
4305     // There's a chance that responding to this event will run a nested event loop, and
4306     // fetching a new event might release the old one. Retaining and then autoreleasing
4307     // the current event prevents that from causing a problem inside WebKit or AppKit code.
4308     [[event retain] autorelease];
4309
4310     BOOL eventWasSentToWebCore = (_private->keyDownEvent == event);
4311     BOOL ret = NO;
4312
4313     [_private->keyDownEvent release];
4314     _private->keyDownEvent = [event retain];
4315     
4316     [self retain];
4317
4318     // Pass command-key combos through WebCore if there is a key binding available for
4319     // this event. This lets web pages have a crack at intercepting command-modified keypresses.
4320     // But don't do it if we have already handled the event.
4321     // Pressing Esc results in a fake event being sent - don't pass it to WebCore.
4322     if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder])
4323         if (Frame* frame = core([self _frame]))
4324             ret = frame->eventHandler()->keyEvent(event);
4325
4326     if (!ret)
4327         ret = [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event];
4328
4329     [self release];
4330     
4331     return ret;
4332 }
4333
4334 - (void)copyFont:(id)sender
4335 {
4336     COMMAND_PROLOGUE
4337
4338     // Put RTF with font attributes on the pasteboard.
4339     // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
4340     NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard];
4341     [fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil];
4342     [fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType];
4343 }
4344
4345 - (void)pasteFont:(id)sender
4346 {
4347     COMMAND_PROLOGUE
4348
4349     // Read RTF with font attributes from the pasteboard.
4350     // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font.
4351     [self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:EditActionPasteFont];
4352 }
4353
4354 - (void)pasteAsRichText:(id)sender
4355 {
4356     COMMAND_PROLOGUE
4357
4358     // Since rich text always beats plain text when both are on the pasteboard, it's not
4359     // clear how this is different from plain old paste.
4360     [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO];
4361 }
4362
4363 - (NSFont *)_originalFontA
4364 {
4365     return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f];
4366 }
4367
4368 - (NSFont *)_originalFontB
4369 {
4370     return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:NSFontItalicTrait weight:STANDARD_BOLD_WEIGHT size:12.0f];
4371 }
4372
4373 - (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b
4374 {
4375     // Since there's no way to directly ask NSFontManager what style change it's going to do
4376     // we instead pass two "specimen" fonts to it and let it change them. We then deduce what
4377     // style change it was doing by looking at what happened to each of the two fonts.
4378     // So if it was making the text bold, both fonts will be bold after the fact.
4379
4380     if (a == nil || b == nil)
4381         return;
4382
4383     NSFontManager *fm = [NSFontManager sharedFontManager];
4384
4385     NSFont *oa = [self _originalFontA];
4386
4387     NSString *aFamilyName = [a familyName];
4388     NSString *bFamilyName = [b familyName];
4389
4390     int aPointSize = (int)[a pointSize];
4391     int bPointSize = (int)[b pointSize];
4392
4393     int aWeight = [fm weightOfFont:a];
4394     int bWeight = [fm weightOfFont:b];
4395
4396     BOOL aIsItalic = ([fm traitsOfFont:a] & NSItalicFontMask) != 0;
4397     BOOL bIsItalic = ([fm traitsOfFont:b] & NSItalicFontMask) != 0;
4398
4399     BOOL aIsBold = aWeight > MIN_BOLD_WEIGHT;
4400
4401     if ([aFamilyName isEqualToString:bFamilyName]) {
4402         NSString *familyNameForCSS = aFamilyName;
4403
4404         // The family name may not be specific enough to get us the font specified.
4405         // In some cases, the only way to get exactly what we are looking for is to use
4406         // the Postscript name.
4407         
4408         // Find the font the same way the rendering code would later if it encountered this CSS.
4409         NSFontTraitMask traits = aIsItalic ? NSFontItalicTrait : 0;
4410         int weight = aIsBold ? STANDARD_BOLD_WEIGHT : STANDARD_WEIGHT;
4411         NSFont *foundFont = [WebFontCache fontWithFamily:aFamilyName traits:traits weight:weight size:aPointSize];
4412
4413         // If we don't find a font with the same Postscript name, then we'll have to use the
4414         // Postscript name to make the CSS specific enough.
4415         if (![[foundFont fontName] isEqualToString:[a fontName]])
4416             familyNameForCSS = [a fontName];
4417
4418         // FIXME: Need more sophisticated escaping code if we want to handle family names
4419         // with characters like single quote or backslash in their names.
4420         [style setFontFamily:[NSString stringWithFormat:@"'%@'", familyNameForCSS]];
4421     }
4422
4423     int soa = (int)[oa pointSize];
4424     if (aPointSize == bPointSize)
4425         [style setFontSize:[NSString stringWithFormat:@"%dpx", aPointSize]];
4426     else if (aPointSize < soa)
4427         [style _setFontSizeDelta:@"-1px"];
4428     else if (aPointSize > soa)
4429         [style _setFontSizeDelta:@"1px"];
4430
4431     // FIXME: Map to the entire range of CSS weight values.
4432     if (aWeight == bWeight)
4433         [style setFontWeight:aIsBold ? @"bold" : @"normal"];
4434
4435     if (aIsItalic == bIsItalic)
4436         [style setFontStyle:aIsItalic ? @"italic" :  @"normal"];
4437 }
4438
4439 - (DOMCSSStyleDeclaration *)_styleFromFontManagerOperation
4440 {
4441     DOMCSSStyleDeclaration *style = [self _emptyStyle];
4442
4443     NSFontManager *fm = [NSFontManager sharedFontManager];
4444
4445     NSFont *oa = [self _originalFontA];
4446     NSFont *ob = [self _originalFontB];    
4447     [self _addToStyle:style fontA:[fm convertFont:oa] fontB:[fm convertFont:ob]];
4448
4449     return style;
4450 }
4451
4452 - (void)changeFont:(id)sender
4453 {
4454     COMMAND_PROLOGUE
4455
4456     [self _applyStyleToSelection:[self _styleFromFontManagerOperation] withUndoAction:EditActionSetFont];
4457 }
4458
4459 - (DOMCSSStyleDeclaration *)_styleForAttributeChange:(id)sender
4460 {
4461     DOMCSSStyleDeclaration *style = [self _emptyStyle];
4462
4463     NSShadow *shadow = [[NSShadow alloc] init];
4464     [shadow setShadowOffset:NSMakeSize(1, 1)];
4465
4466     NSDictionary *oa = [NSDictionary dictionaryWithObjectsAndKeys:
4467         [self _originalFontA], NSFontAttributeName,
4468         nil];
4469     NSDictionary *ob = [NSDictionary dictionaryWithObjectsAndKeys:
4470         [NSColor blackColor], NSBackgroundColorAttributeName,
4471         [self _originalFontB], NSFontAttributeName,
4472         [NSColor whiteColor], NSForegroundColorAttributeName,
4473         shadow, NSShadowAttributeName,
4474         [NSNumber numberWithInt:NSUnderlineStyleSingle], NSStrikethroughStyleAttributeName,
4475         [NSNumber numberWithInt:1], NSSuperscriptAttributeName,
4476         [NSNumber numberWithInt:NSUnderlineStyleSingle], NSUnderlineStyleAttributeName,
4477         nil];
4478
4479     [shadow release];
4480
4481 #if 0
4482
4483 NSObliquenessAttributeName        /* float; skew to be applied to glyphs, default 0: no skew */
4484     // font-style, but that is just an on-off switch
4485
4486 NSExpansionAttributeName          /* float; log of expansion factor to be applied to glyphs, default 0: no expansion */
4487     // font-stretch?
4488
4489 NSKernAttributeName               /* float, amount to modify default kerning, if 0, kerning off */
4490     // letter-spacing? probably not good enough
4491
4492 NSUnderlineColorAttributeName     /* NSColor, default nil: same as foreground color */
4493 NSStrikethroughColorAttributeName /* NSColor, default nil: same as foreground color */
4494     // text-decoration-color?
4495
4496 NSLigatureAttributeName           /* int, default 1: default ligatures, 0: no ligatures, 2: all ligatures */
4497 NSBaselineOffsetAttributeName     /* float, in points; offset from baseline, default 0 */
4498 NSStrokeWidthAttributeName        /* float, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0) */
4499 NSStrokeColorAttributeName        /* NSColor, default nil: same as foreground color */
4500     // need extensions?
4501
4502 #endif
4503     
4504     NSDictionary *a = [sender convertAttributes:oa];
4505     NSDictionary *b = [sender convertAttributes:ob];
4506
4507     NSColor *ca = [a objectForKey:NSBackgroundColorAttributeName];
4508     NSColor *cb = [b objectForKey:NSBackgroundColorAttributeName];
4509     if (ca == cb) {
4510         [style setBackgroundColor:[self _colorAsString:ca]];
4511     }
4512
4513     [self _addToStyle:style fontA:[a objectForKey:NSFontAttributeName] fontB:[b objectForKey:NSFontAttributeName]];
4514
4515     ca = [a objectForKey:NSForegroundColorAttributeName];
4516     cb = [b objectForKey:NSForegroundColorAttributeName];
4517     if (ca == cb) {
4518         [style setColor:[self _colorAsString:ca]];
4519     }
4520
4521     NSShadow *sha = [a objectForKey:NSShadowAttributeName];
4522     if (sha)
4523         [style setTextShadow:[self _shadowAsString:sha]];
4524     else if ([b objectForKey:NSShadowAttributeName] == nil)
4525         [style setTextShadow:@"none"];
4526
4527     int sa = [[a objectForKey:NSStrikethroughStyleAttributeName] intValue];
4528     int sb = [[b objectForKey:NSStrikethroughStyleAttributeName] intValue];
4529     if (sa == sb) {
4530         if (sa == NSUnderlineStyleNone)
4531             [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; 
4532             // we really mean "no line-through" rather than "none"
4533         else
4534             [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""];
4535             // we really mean "add line-through" rather than "line-through"
4536     }
4537
4538     sa = [[a objectForKey:NSSuperscriptAttributeName] intValue];
4539     sb = [[b objectForKey:NSSuperscriptAttributeName] intValue];
4540     if (sa == sb) {
4541         if (sa > 0)
4542             [style setVerticalAlign:@"super"];
4543         else if (sa < 0)
4544             [style setVerticalAlign:@"sub"];
4545         else
4546             [style setVerticalAlign:@"baseline"];
4547     }
4548
4549     int ua = [[a objectForKey:NSUnderlineStyleAttributeName] intValue];
4550     int ub = [[b objectForKey:NSUnderlineStyleAttributeName] intValue];
4551     if (ua == ub) {
4552         if (ua == NSUnderlineStyleNone)
4553             [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""];
4554             // we really mean "no underline" rather than "none"
4555         else
4556             [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""];
4557             // we really mean "add underline" rather than "underline"
4558     }
4559
4560     return style;
4561 }
4562
4563 - (void)changeAttributes:(id)sender
4564 {
4565     COMMAND_PROLOGUE
4566
4567     [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes];
4568 }
4569
4570 - (DOMCSSStyleDeclaration *)_styleFromColorPanelWithSelector:(SEL)selector
4571 {
4572     DOMCSSStyleDeclaration *style = [self _emptyStyle];
4573
4574     ASSERT([style respondsToSelector:selector]);
4575     [style performSelector:selector withObject:[self _colorAsString:[[NSColorPanel sharedColorPanel] color]]];
4576     
4577     return style;
4578 }
4579
4580 - (EditAction)_undoActionFromColorPanelWithSelector:(SEL)selector
4581 {
4582     if (selector == @selector(setBackgroundColor:))
4583         return EditActionSetBackgroundColor;    
4584     return EditActionSetColor;
4585 }
4586
4587 - (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range
4588 {
4589     DOMCSSStyleDeclaration *style = [self _styleFromColorPanelWithSelector:selector];
4590     WebView *webView = [self _webView];
4591     if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:range])
4592         if (Frame* coreFrame = core([self _frame]))
4593             coreFrame->editor()->applyStyle(core(style), [self _undoActionFromColorPanelWithSelector:selector]);
4594 }
4595
4596 - (void)changeDocumentBackgroundColor:(id)sender
4597 {
4598     COMMAND_PROLOGUE
4599
4600     // Mimicking NSTextView, this method sets the background color for the
4601     // entire document. There is no NSTextView API for setting the background
4602     // color on the selected range only. Note that this method is currently
4603     // never called from the UI (see comment in changeColor:).
4604     // FIXME: this actually has no effect when called, probably due to 3654850. _documentRange seems
4605     // to do the right thing because it works in startSpeaking:, and I know setBackgroundColor: does the
4606     // right thing because I tested it with [self _selectedRange].
4607     // FIXME: This won't actually apply the style to the entire range here, because it ends up calling
4608     // [frame _applyStyle:], which operates on the current selection. To make this work right, we'll
4609     // need to save off the selection, temporarily set it to the entire range, make the change, then
4610     // restore the old selection.
4611     [self _changeCSSColorUsingSelector:@selector(setBackgroundColor:) inRange:[self _documentRange]];
4612 }
4613
4614 - (void)changeColor:(id)sender
4615 {
4616     COMMAND_PROLOGUE
4617
4618     // FIXME: in NSTextView, this method calls changeDocumentBackgroundColor: when a
4619     // private call has earlier been made by [NSFontFontEffectsBox changeColor:], see 3674493. 
4620     // AppKit will have to be revised to allow this to work with anything that isn't an 
4621     // NSTextView. However, this might not be required for Tiger, since the background-color 
4622     // changing box in the font panel doesn't work in Mail (3674481), though it does in TextEdit.
4623     [self _applyStyleToSelection:[self _styleFromColorPanelWithSelector:@selector(setColor:)] 
4624                   withUndoAction:EditActionSetColor];
4625 }
4626
4627 - (void)_changeWordCaseWithSelector:(SEL)selector
4628 {
4629     if (![self _canEdit])
4630         return;
4631
4632     WebFrame *frame = [self _frame];
4633     [self selectWord:nil];
4634     NSString *word = [[frame _selectedString] performSelector:selector];
4635     // FIXME: Does this need a different action context other than "typed"?
4636     if ([self _shouldReplaceSelectionWithText:word givenAction:WebViewInsertActionTyped])
4637         [frame _replaceSelectionWithText:word selectReplacement:NO smartReplace:NO];
4638 }
4639
4640 - (void)uppercaseWord:(id)sender
4641 {
4642     COMMAND_PROLOGUE
4643
4644     [self _changeWordCaseWithSelector:@selector(uppercaseString)];
4645 }
4646
4647 - (void)lowercaseWord:(id)sender
4648 {
4649     COMMAND_PROLOGUE
4650
4651     [self _changeWordCaseWithSelector:@selector(lowercaseString)];
4652 }
4653
4654 - (void)capitalizeWord:(id)sender
4655 {
4656     COMMAND_PROLOGUE
4657
4658     [self _changeWordCaseWithSelector:@selector(capitalizedString)];
4659 }
4660
4661 - (void)complete:(id)sender
4662 {
4663     COMMAND_PROLOGUE
4664
4665     if (![self _canEdit])
4666         return;
4667     if (!_private->completionController)
4668         _private->completionController = [[WebTextCompletionController alloc] initWithWebView:[self _webView] HTMLView:self];
4669     [_private->completionController doCompletion];
4670 }
4671
4672 - (void)checkSpelling:(id)sender
4673 {
4674     COMMAND_PROLOGUE
4675
4676     if (Frame* coreFrame = core([self _frame]))
4677         coreFrame->editor()->advanceToNextMisspelling();
4678 }
4679
4680 - (void)showGuessPanel:(id)sender
4681 {
4682     COMMAND_PROLOGUE
4683
4684     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
4685     if (!checker) {
4686         LOG_ERROR("No NSSpellChecker");
4687         return;
4688     }
4689     
4690     NSPanel *spellingPanel = [checker spellingPanel];
4691     if ([spellingPanel isVisible]) {
4692         [spellingPanel orderOut:sender];
4693         return;
4694     }
4695     
4696     if (Frame* coreFrame = core([self _frame]))
4697         coreFrame->editor()->advanceToNextMisspelling(true);
4698     [spellingPanel orderFront:sender];
4699 }
4700
4701 - (void)_changeSpellingToWord:(NSString *)newWord
4702 {
4703     if (![self _canEdit])
4704         return;
4705
4706     // Don't correct to empty string.  (AppKit checked this, we might as well too.)
4707     if (![NSSpellChecker sharedSpellChecker]) {
4708         LOG_ERROR("No NSSpellChecker");
4709         return;
4710     }
4711     
4712     if ([newWord isEqualToString:@""])
4713         return;
4714
4715     if ([self _shouldReplaceSelectionWithText:newWord givenAction:WebViewInsertActionPasted])
4716         [[self _frame] _replaceSelectionWithText:newWord selectReplacement:YES smartReplace:NO];
4717 }
4718
4719 - (void)changeSpelling:(id)sender
4720 {
4721     COMMAND_PROLOGUE
4722
4723     [self _changeSpellingToWord:[[sender selectedCell] stringValue]];
4724 }
4725
4726 - (void)performFindPanelAction:(id)sender
4727 {
4728     COMMAND_PROLOGUE
4729
4730     // Implementing this will probably require copying all of NSFindPanel.h and .m.
4731     // We need *almost* the same thing as AppKit, but not quite.
4732     LOG_ERROR("unimplemented");
4733 }
4734
4735 - (void)startSpeaking:(id)sender
4736 {
4737     COMMAND_PROLOGUE
4738
4739     WebFrame *frame = [self _frame];
4740     DOMRange *range = [self _selectedRange];
4741     if (!range || [range collapsed])
4742         range = [self _documentRange];
4743     [NSApp speakString:[frame _stringForRange:range]];
4744 }
4745
4746 - (void)stopSpeaking:(id)sender
4747 {
4748     COMMAND_PROLOGUE
4749
4750     [NSApp stopSpeaking:sender];
4751 }
4752
4753 - (void)toggleBaseWritingDirection:(id)sender
4754 {
4755     COMMAND_PROLOGUE
4756
4757     if (![self _canEdit])
4758         return;
4759     
4760     Frame* coreFrame = core([self _frame]);
4761     if (!coreFrame)
4762         return;
4763
4764     WritingDirection direction = RightToLeftWritingDirection;
4765     switch (coreFrame->editor()->baseWritingDirectionForSelectionStart()) {
4766         case NSWritingDirectionLeftToRight:
4767             break;
4768         case NSWritingDirectionRightToLeft:
4769             direction = LeftToRightWritingDirection;
4770             break;
4771         // The writingDirectionForSelectionStart method will never return "natural". It
4772         // will always return a concrete direction. So, keep the compiler happy, and assert not reached.
4773         case NSWritingDirectionNatural:
4774             ASSERT_NOT_REACHED();
4775             break;
4776     }
4777
4778     if (Frame* coreFrame = core([self _frame]))
4779         coreFrame->editor()->setBaseWritingDirection(direction);
4780 }
4781
4782 - (void)changeBaseWritingDirection:(id)sender
4783 {
4784     COMMAND_PROLOGUE
4785
4786     if (![self _canEdit])
4787         return;
4788     
4789     NSWritingDirection writingDirection = static_cast<NSWritingDirection>([sender tag]);
4790     
4791     // We disable the menu item that performs this action because we can't implement
4792     // NSWritingDirectionNatural's behavior using CSS.
4793     ASSERT(writingDirection != NSWritingDirectionNatural);
4794
4795     if (Frame* coreFrame = core([self _frame]))
4796         coreFrame->editor()->setBaseWritingDirection(writingDirection == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection);
4797 }
4798
4799 static BOOL writingDirectionKeyBindingsEnabled()
4800 {
4801 #ifndef BUILDING_ON_LEOPARD
4802     return YES;
4803 #else
4804     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
4805     return [defaults boolForKey:@"NSAllowsBaseWritingDirectionKeyBindings"] || [defaults boolForKey:@"AppleTextDirection"];
4806 #endif
4807 }
4808
4809 - (void)_changeBaseWritingDirectionTo:(NSWritingDirection)direction
4810 {
4811     if (![self _canEdit])
4812         return;
4813
4814     static BOOL bindingsEnabled = writingDirectionKeyBindingsEnabled();
4815
4816     if (!bindingsEnabled) {
4817         NSBeep();
4818         return;
4819     }
4820
4821     if (Frame* coreFrame = core([self _frame]))
4822         coreFrame->editor()->setBaseWritingDirection(direction == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection);
4823 }
4824
4825 - (void)makeBaseWritingDirectionLeftToRight:(id)sender
4826 {
4827     COMMAND_PROLOGUE
4828
4829     [self _changeBaseWritingDirectionTo:NSWritingDirectionLeftToRight];
4830 }
4831
4832 - (void)makeBaseWritingDirectionRightToLeft:(id)sender
4833 {
4834     COMMAND_PROLOGUE
4835
4836     [self _changeBaseWritingDirectionTo:NSWritingDirectionRightToLeft];
4837 }
4838
4839 #ifdef BUILDING_ON_LEOPARD
4840 - (void)changeBaseWritingDirectionToLTR:(id)sender
4841 {
4842     [self makeBaseWritingDirectionLeftToRight:sender];
4843 }
4844
4845 - (void)changeBaseWritingDirectionToRTL:(id)sender
4846 {
4847     [self makeBaseWritingDirectionRightToLeft:sender];
4848 }
4849 #endif
4850
4851 - (void)makeBaseWritingDirectionNatural:(id)sender
4852 {
4853     LOG_ERROR("Sent from %@.", sender);
4854 }
4855
4856 #if 0
4857
4858 // CSS does not have a way to specify an outline font, which may make this difficult to implement.
4859 // Maybe a special case of text-shadow?
4860 - (void)outline:(id)sender;
4861
4862 // This is part of table support, which may be in NSTextView for Tiger.
4863 // It's probably simple to do the equivalent thing for WebKit.
4864 - (void)insertTable:(id)sender;
4865
4866 // This could be important.
4867 - (void)toggleTraditionalCharacterShape:(id)sender;
4868
4869 // I'm not sure what the equivalents of these in the web world are.
4870 - (void)insertLineSeparator:(id)sender;
4871 - (void)insertPageBreak:(id)sender;
4872
4873 // These methods are not implemented in NSTextView yet at the time of this writing.
4874 - (void)changeCaseOfLetter:(id)sender;
4875 - (void)transposeWords:(id)sender;
4876
4877 #endif
4878
4879
4880 // Override this so that AppKit will send us arrow keys as key down events so we can
4881 // support them via the key bindings mechanism.
4882 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
4883 {
4884     bool haveWebCoreFrame = core([self _frame]);
4885
4886     // If we have a frame, our keyDown method will handle key bindings after sending
4887     // the event through the DOM, so ask AppKit not to do its early special key binding
4888     // mapping. If we don't have a frame, just let things work the normal way without
4889     // a keyDown.
4890     return haveWebCoreFrame;
4891 }
4892
4893
4894 - (void)_updateControlTints
4895 {
4896     Frame* frame = core([self _frame]);
4897     if (!frame)
4898         return;
4899     FrameView* view = frame->view();
4900     if (!view)
4901         return;
4902     view->updateControlTints();
4903 }
4904
4905 // Despite its name, this is called at different times than windowDidBecomeKey is.
4906 // It takes into account all the other factors that determine when NSCell draws
4907 // with different tints, so it's the right call to use for control tints. We'd prefer
4908 // to do this with API. <rdar://problem/5136760>
4909 - (void)_windowChangedKeyState
4910 {
4911     if (pthread_main_np())
4912         [self _updateControlTints];
4913     else
4914         [self performSelectorOnMainThread:@selector(_updateControlTints) withObject:nil waitUntilDone:NO];
4915
4916     [super _windowChangedKeyState];
4917 }
4918
4919 - (void)otherMouseDown:(NSEvent *)event
4920 {
4921     if ([event buttonNumber] == 2)
4922         [self mouseDown:event];
4923     else
4924         [super otherMouseDown:event];
4925 }
4926
4927 - (void)otherMouseDragged:(NSEvent *)event
4928 {
4929     if ([event buttonNumber] == 2)
4930         [self mouseDragged:event];
4931     else
4932         [super otherMouseDragged:event];
4933 }
4934
4935 - (void)otherMouseUp:(NSEvent *)event
4936 {
4937     if ([event buttonNumber] == 2)
4938         [self mouseUp:event];
4939     else
4940         [super otherMouseUp:event];
4941 }
4942
4943 @end
4944
4945 @implementation WebHTMLView (WebInternal)
4946
4947 - (void)_selectionChanged
4948 {
4949     [self _updateSelectionForInputManager];
4950     [self _updateFontPanel];
4951     if (Frame* coreFrame = core([self _frame]))
4952         coreFrame->editor()->setStartNewKillRingSequence(true);
4953 }
4954
4955 - (void)_updateFontPanel
4956 {
4957     // FIXME: NSTextView bails out if becoming or resigning first responder, for which it has ivar flags. Not
4958     // sure if we need to do something similar.
4959
4960     if (![self _canEdit])
4961         return;
4962
4963     NSWindow *window = [self window];
4964     // FIXME: is this first-responder check correct? What happens if a subframe is editable and is first responder?
4965     if (![window isKeyWindow] || [window firstResponder] != self)
4966         return;
4967
4968     bool multipleFonts = false;
4969     NSFont *font = nil;
4970     if (Frame* coreFrame = core([self _frame])) {
4971         if (const SimpleFontData* fd = coreFrame->editor()->fontForSelection(multipleFonts))
4972             font = fd->getNSFont();
4973     }
4974
4975     // FIXME: for now, return a bogus font that distinguishes the empty selection from the non-empty
4976     // selection. We should be able to remove this once the rest of this code works properly.
4977     if (font == nil)
4978         font = [self _hasSelection] ? [NSFont menuFontOfSize:23] : [NSFont toolTipsFontOfSize:17];
4979     ASSERT(font != nil);
4980
4981     [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:multipleFonts];
4982
4983     // FIXME: we don't keep track of selected attributes, or set them on the font panel. This
4984     // appears to have no effect on the UI. E.g., underlined text in Mail or TextEdit is
4985     // not reflected in the font panel. Maybe someday this will change.
4986 }
4987
4988 - (BOOL)_canSmartCopyOrDelete
4989 {
4990     if (![[self _webView] smartInsertDeleteEnabled])
4991         return NO;
4992     Frame* coreFrame = core([self _frame]);
4993     return coreFrame && coreFrame->selection()->granularity() == WordGranularity;
4994 }
4995
4996 - (NSEvent *)_mouseDownEvent
4997 {
4998     return _private->mouseDownEvent;
4999 }
5000
5001 - (id<WebHTMLHighlighter>)_highlighterForType:(NSString*)type
5002 {
5003     return [_private->highlighters objectForKey:type];
5004 }
5005
5006 - (WebFrame *)_frame
5007 {
5008     return [_private->dataSource webFrame];
5009 }
5010
5011 - (void)closeIfNotCurrentView
5012 {
5013     if ([[[self _frame] frameView] documentView] != self)
5014         [self close];
5015 }
5016
5017 - (DOMDocumentFragment*)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
5018 {
5019     return [self _documentFragmentFromPasteboard:pasteboard inContext:nil allowPlainText:NO];
5020 }
5021
5022
5023 - (BOOL)isGrammarCheckingEnabled
5024 {
5025     // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
5026     // the AppKit code checks the first responder.
5027     return [[self _webView] isGrammarCheckingEnabled];
5028 }
5029
5030 - (void)setGrammarCheckingEnabled:(BOOL)flag
5031 {
5032     // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
5033     // the AppKit code checks the first responder.
5034     [[self _webView] setGrammarCheckingEnabled:flag];
5035 }
5036
5037 - (void)toggleGrammarChecking:(id)sender
5038 {
5039     // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because
5040     // the AppKit code checks the first responder.
5041     [[self _webView] toggleGrammarChecking:sender];
5042 }
5043
5044
5045 static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point)
5046 {
5047     NSArray *screens = [NSScreen screens];
5048     
5049     if ([screens count] == 0) {
5050         // You could theoretically get here if running with no monitor, in which case it doesn't matter
5051         // much where the "on-screen" point is.
5052         return CGPointMake(point.x, point.y);
5053     }
5054     
5055     // Flip the y coordinate from the top of the menu bar screen -- see 4636390
5056     return CGPointMake(point.x, NSMaxY([(NSScreen *)[screens objectAtIndex:0] frame]) - point.y);
5057 }
5058
5059
5060 #ifndef BUILDING_ON_LEOPARD
5061
5062 - (void)orderFrontSubstitutionsPanel:(id)sender
5063 {
5064     COMMAND_PROLOGUE
5065
5066     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
5067     if (!checker) {
5068         LOG_ERROR("No NSSpellChecker");
5069         return;
5070     }
5071     
5072     NSPanel *substitutionsPanel = [checker substitutionsPanel];
5073     if ([substitutionsPanel isVisible]) {
5074         [substitutionsPanel orderOut:sender];
5075         return;
5076     }
5077     [substitutionsPanel orderFront:sender];
5078 }
5079
5080 // FIXME 4799134: WebView is the bottleneck for this logic, but we must implement these methods here because
5081 // the AppKit code checks the first responder.
5082
5083 - (BOOL)smartInsertDeleteEnabled
5084 {
5085     return [[self _webView] smartInsertDeleteEnabled];
5086 }
5087
5088 - (void)setSmartInsertDeleteEnabled:(BOOL)flag
5089 {
5090     [[self _webView] setSmartInsertDeleteEnabled:flag];
5091 }
5092
5093 - (void)toggleSmartInsertDelete:(id)sender
5094 {
5095     [[self _webView] toggleSmartInsertDelete:sender];
5096 }
5097
5098 - (BOOL)isAutomaticQuoteSubstitutionEnabled
5099 {
5100     return [[self _webView] isAutomaticQuoteSubstitutionEnabled];
5101 }
5102
5103 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
5104 {
5105     [[self _webView] setAutomaticQuoteSubstitutionEnabled:flag];
5106 }
5107
5108 - (void)toggleAutomaticQuoteSubstitution:(id)sender
5109 {
5110     [[self _webView] toggleAutomaticQuoteSubstitution:sender];
5111 }
5112
5113 - (BOOL)isAutomaticLinkDetectionEnabled
5114 {
5115     return [[self _webView] isAutomaticLinkDetectionEnabled];
5116 }
5117
5118 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
5119 {
5120     [[self _webView] setAutomaticLinkDetectionEnabled:flag];
5121 }
5122
5123 - (void)toggleAutomaticLinkDetection:(id)sender
5124 {
5125     [[self _webView] toggleAutomaticLinkDetection:sender];
5126 }
5127
5128 - (BOOL)isAutomaticDashSubstitutionEnabled
5129 {
5130     return [[self _webView] isAutomaticDashSubstitutionEnabled];
5131 }
5132
5133 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
5134 {
5135     [[self _webView] setAutomaticDashSubstitutionEnabled:flag];
5136 }
5137
5138 - (void)toggleAutomaticDashSubstitution:(id)sender
5139 {
5140     [[self _webView] toggleAutomaticDashSubstitution:sender];
5141 }
5142
5143 - (BOOL)isAutomaticTextReplacementEnabled
5144 {
5145     return [[self _webView] isAutomaticTextReplacementEnabled];
5146 }
5147
5148 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
5149 {
5150     [[self _webView] setAutomaticTextReplacementEnabled:flag];
5151 }
5152
5153 - (void)toggleAutomaticTextReplacement:(id)sender
5154 {
5155     [[self _webView] toggleAutomaticTextReplacement:sender];
5156 }
5157
5158 - (BOOL)isAutomaticSpellingCorrectionEnabled
5159 {
5160     return [[self _webView] isAutomaticSpellingCorrectionEnabled];
5161 }
5162
5163 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag
5164 {
5165     [[self _webView] setAutomaticSpellingCorrectionEnabled:flag];
5166 }
5167
5168 - (void)toggleAutomaticSpellingCorrection:(id)sender
5169 {
5170     [[self _webView] toggleAutomaticSpellingCorrection:sender];
5171 }
5172
5173 #endif
5174
5175 - (void)_lookUpInDictionaryFromMenu:(id)sender
5176 {
5177     // Dictionary API will accept a whitespace-only string and display UI as if it were real text,
5178     // so bail out early to avoid that.
5179     if ([[[self selectedString] _webkit_stringByTrimmingWhitespace] length] == 0)
5180         return;
5181
5182     NSAttributedString *attrString = [self selectedAttributedString];
5183
5184     Frame* coreFrame = core([self _frame]);
5185     if (!coreFrame)
5186         return;
5187
5188     NSRect rect = coreFrame->selection()->bounds();
5189
5190     NSDictionary *attributes = [attrString fontAttributesInRange:NSMakeRange(0,1)];
5191     NSFont *font = [attributes objectForKey:NSFontAttributeName];
5192     if (font)
5193         rect.origin.y += [font ascender];
5194
5195 #ifndef BUILDING_ON_LEOPARD
5196     [self showDefinitionForAttributedString:attrString atPoint:rect.origin];
5197     return;
5198 #endif
5199
5200     // We soft link to get the function that displays the dictionary (either pop-up window or app) to avoid the performance
5201     // penalty of linking to another framework. This function changed signature as well as framework between Tiger and Leopard,
5202     // so the two cases are handled separately.
5203
5204     typedef void (*ServiceWindowShowFunction)(id unusedDictionaryRef, id inWordString, CFRange selectionRange, id unusedFont, CGPoint textOrigin, Boolean verticalText, id unusedTransform);
5205     const char *frameworkPath = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
5206     const char *functionName = "HIDictionaryWindowShow";
5207
5208     static bool lookedForFunction = false;
5209     static ServiceWindowShowFunction dictionaryServiceWindowShow = NULL;
5210
5211     if (!lookedForFunction) {
5212         void* langAnalysisFramework = dlopen(frameworkPath, RTLD_LAZY);
5213         ASSERT(langAnalysisFramework);
5214         if (langAnalysisFramework)
5215             dictionaryServiceWindowShow = (ServiceWindowShowFunction)dlsym(langAnalysisFramework, functionName);
5216         lookedForFunction = true;
5217     }
5218
5219     ASSERT(dictionaryServiceWindowShow);
5220     if (!dictionaryServiceWindowShow) {
5221         NSLog(@"Couldn't find the %s function in %s", functionName, frameworkPath); 
5222         return;
5223     }
5224
5225     // The HIDictionaryWindowShow function requires the origin, in CG screen coordinates, of the first character of text in the selection.
5226     // FIXME 4945808: We approximate this in a way that works well when a single word is selected, and less well in some other cases
5227     // (but no worse than we did in Tiger)
5228     NSPoint windowPoint = [self convertPoint:rect.origin toView:nil];
5229     NSPoint screenPoint = [[self window] convertBaseToScreen:windowPoint];
5230
5231     dictionaryServiceWindowShow(nil, attrString, CFRangeMake(0, [attrString length]), nil, 
5232                                 coreGraphicsScreenPointForAppKitScreenPoint(screenPoint), false, nil);
5233 }
5234
5235 - (void)_hoverFeedbackSuspendedChanged
5236 {
5237     [self _updateMouseoverWithFakeEvent];
5238 }
5239
5240 - (void)_executeSavedKeypressCommands
5241 {
5242     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5243     if (!parameters || parameters->event->keypressCommands().isEmpty())
5244         return;
5245
5246     // We could be called again if the execution of one command triggers a call to selectedRange.
5247     // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result
5248     if (parameters->executingSavedKeypressCommands)
5249         return;
5250     
5251     // Avoid an infinite loop that would occur if executing a command appended it to event->keypressCommands() again.
5252     bool wasSavingCommands = parameters->shouldSaveCommands;
5253     parameters->shouldSaveCommands = false;
5254     parameters->executingSavedKeypressCommands = true;
5255     
5256     const Vector<KeypressCommand>& commands = parameters->event->keypressCommands();
5257
5258     for (size_t i = 0; i < commands.size(); ++i) {
5259         if (commands[i].commandName == "insertText:")
5260             [self insertText:commands[i].text];
5261         else if (commands[i].commandName == "noop:")
5262             ; // Do nothing. This case can be removed once <rdar://problem/9025012> is fixed.
5263         else
5264             [self doCommandBySelector:NSSelectorFromString(commands[i].commandName)];
5265     }
5266     parameters->event->keypressCommands().clear();
5267     parameters->shouldSaveCommands = wasSavingCommands;
5268     parameters->executingSavedKeypressCommands = false;
5269 }
5270
5271 - (BOOL)_interpretKeyEvent:(KeyboardEvent*)event savingCommands:(BOOL)savingCommands
5272 {
5273     ASSERT(core([self _frame]) == event->target()->toNode()->document()->frame());
5274     ASSERT(!savingCommands || event->keypressCommands().isEmpty()); // Save commands once for each event.
5275
5276     WebHTMLViewInterpretKeyEventsParameters parameters;
5277     parameters.eventInterpretationHadSideEffects = false;
5278     parameters.shouldSaveCommands = savingCommands;
5279     parameters.executingSavedKeypressCommands = false;
5280     // If we're intercepting the initial IM call we assume that the IM has consumed the event, 
5281     // and only change this assumption if one of the NSTextInput/Responder callbacks is used.
5282     // We assume the IM will *not* consume hotkey sequences
5283     parameters.consumedByIM = savingCommands && !event->metaKey();
5284
5285     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
5286     if (!platformEvent)
5287         return NO;
5288
5289     NSEvent *macEvent = platformEvent->macEvent();
5290     if ([macEvent type] == NSKeyDown && [_private->completionController filterKeyDown:macEvent])
5291         return YES;
5292     
5293     if ([macEvent type] == NSFlagsChanged)
5294         return NO;
5295     
5296     parameters.event = event;
5297     _private->interpretKeyEventsParameters = &parameters;
5298     const Vector<KeypressCommand>& commands = event->keypressCommands();
5299
5300     if (savingCommands) {
5301         // AppKit will respond with a series of NSTextInput protocol method calls. There are three groups that we heuristically differentiate:
5302         // 1. Key Bindings. Only doCommandBySelector: and insertText: calls will be made, which we save in the event for execution
5303         // after DOM dispatch. This is safe, because neither returns a result, so there is no branching on AppKit side.
5304         // 2. Plain text input. Here as well, we need to dispatch DOM events prior to inserting text, so we save the insertText: command.
5305         // 3. Input method processing. An IM can make any NSTextInput calls, and can base its decisions on results it gets, so we must
5306         // execute the calls immediately. DOM events like keydown are tweaked to have keyCode of 229, and canceling them has no effect.
5307         // Unfortunately, there is no real difference between plain text input and IM processing - for example, AppKit queries hasMarkedText
5308         // when typing with U.S. keyboard, and inserts marked text for dead keys.
5309         [self interpretKeyEvents:[NSArray arrayWithObject:macEvent]];
5310     } else {
5311         // Are there commands that could just cause text insertion if executed via Editor?
5312         // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
5313         // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
5314         // (e.g. Tab that inserts a Tab character, or Enter).
5315         bool haveTextInsertionCommands = false;
5316         for (size_t i = 0; i < commands.size(); ++i) {
5317             if ([self coreCommandBySelector:NSSelectorFromString(commands[i].commandName)].isTextInsertion())
5318                 haveTextInsertionCommands = true;
5319         }
5320         // If there are no text insertion commands, default keydown handler is the right time to execute the commands.
5321         // Keypress (Char event) handler is the latest opportunity to execute.
5322         if (!haveTextInsertionCommands || platformEvent->type() == PlatformKeyboardEvent::Char)
5323             [self _executeSavedKeypressCommands];
5324     }
5325     _private->interpretKeyEventsParameters = 0;
5326
5327     // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
5328     // IM-like actions are handled immediately (so parameters.eventInterpretationHadSideEffects is true), but there are saved commands that
5329     // should be handled like normal text input after DOM event dispatch.
5330     if (!event->keypressCommands().isEmpty())
5331         return NO;
5332
5333     // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
5334     // in which case we should not bubble the event up the DOM.
5335     if (parameters.consumedByIM)
5336         return YES;
5337
5338     // If we have already executed all commands, don't do it again.
5339     return parameters.eventInterpretationHadSideEffects;
5340 }
5341
5342 - (WebCore::CachedImage*)promisedDragTIFFDataSource
5343 {
5344     return _private->promisedDragTIFFDataSource;
5345 }
5346
5347 - (void)setPromisedDragTIFFDataSource:(WebCore::CachedImage*)source
5348 {
5349     if (source)
5350         source->addClient(promisedDataClient());
5351     
5352     if (_private->promisedDragTIFFDataSource)
5353         _private->promisedDragTIFFDataSource->removeClient(promisedDataClient());
5354     _private->promisedDragTIFFDataSource = source;
5355 }
5356
5357 #undef COMMAND_PROLOGUE
5358
5359 - (void)_layoutIfNeeded
5360 {
5361     ASSERT(!_private->subviewsSetAside);
5362
5363     if ([self _needsLayout])
5364         [self layout];
5365 }
5366
5367 - (void)_web_updateLayoutAndStyleIfNeededRecursive
5368 {
5369     WebFrame *webFrame = [self _frame];
5370     Frame* coreFrame = core(webFrame);
5371     if (coreFrame && coreFrame->view())
5372         coreFrame->view()->updateLayoutAndStyleIfNeededRecursive();
5373 }
5374
5375 - (void) _destroyAllWebPlugins
5376 {
5377     [[self _pluginController] destroyAllPlugins];
5378 }
5379
5380 - (BOOL)_needsLayout
5381 {
5382     return [[self _frame] _needsLayout];
5383 }
5384
5385 #if USE(ACCELERATED_COMPOSITING)
5386 - (void)attachRootLayer:(CALayer*)layer
5387 {
5388     if (!_private->layerHostingView) {
5389         NSView* hostingView = [[NSView alloc] initWithFrame:[self bounds]];
5390 #ifndef BUILDING_ON_LEOPARD
5391         [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
5392 #endif
5393         [self addSubview:hostingView];
5394         [hostingView release];
5395         // hostingView is owned by being a subview of self
5396         _private->layerHostingView = hostingView;
5397     }
5398
5399     // Make a container layer, which will get sized/positioned by AppKit and CA.
5400     CALayer* viewLayer = [CALayer layer];
5401
5402 #ifdef BUILDING_ON_LEOPARD
5403     // Turn off default animations.
5404     NSNull *nullValue = [NSNull null];
5405     NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys:
5406                              nullValue, @"anchorPoint",
5407                              nullValue, @"bounds",
5408                              nullValue, @"contents",
5409                              nullValue, @"contentsRect",
5410                              nullValue, @"opacity",
5411                              nullValue, @"position",
5412                              nullValue, @"sublayerTransform",
5413                              nullValue, @"sublayers",
5414                              nullValue, @"transform",
5415                              nil];
5416     [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]];
5417 #endif
5418
5419     if ([self layer]) {
5420         // If we are in a layer-backed view, we need to manually initialize the geometry for our layer.
5421         [viewLayer setBounds:NSRectToCGRect([_private->layerHostingView bounds])];
5422         [viewLayer setAnchorPoint:CGPointMake(0, [self isFlipped] ? 1 : 0)];
5423         CGPoint layerPosition = NSPointToCGPoint([self convertPointToBase:[_private->layerHostingView frame].origin]);
5424         [viewLayer setPosition:layerPosition];
5425     }
5426     
5427     [_private->layerHostingView setLayer:viewLayer];
5428     [_private->layerHostingView setWantsLayer:YES];
5429     
5430     // Parent our root layer in the container layer
5431     [viewLayer addSublayer:layer];
5432     
5433     if ([[self _webView] _postsAcceleratedCompositingNotifications])
5434         [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:[self _webView] userInfo:nil];
5435     
5436 #ifdef BUILDING_ON_LEOPARD
5437     [viewLayer setSublayerTransform:CATransform3DMakeScale(1, -1, 1)]; // setGeometryFlipped: doesn't exist on Leopard.
5438     [self _updateLayerHostingViewPosition];
5439 #else
5440     // Do geometry flipping here, which flips all the compositing layers so they are top-down.
5441     [viewLayer setGeometryFlipped:YES];
5442 #endif
5443 }
5444
5445 - (void)detachRootLayer
5446 {
5447     if (_private->layerHostingView) {
5448         [_private->layerHostingView setLayer:nil];
5449         [_private->layerHostingView setWantsLayer:NO];
5450         [_private->layerHostingView removeFromSuperview];
5451         _private->layerHostingView = nil;
5452     }
5453 }
5454
5455 #ifdef BUILDING_ON_LEOPARD
5456 // This method is necessary on Leopard to work around <rdar://problem/7067892>.
5457 - (void)_updateLayerHostingViewPosition
5458 {
5459     if (!_private->layerHostingView)
5460         return;
5461     
5462     const CGFloat maxHeight = 2048;
5463     NSRect layerViewFrame = [self bounds];
5464
5465     if (layerViewFrame.size.height > maxHeight) {
5466         // Clamp the size of the view to <= maxHeight to avoid the bug.
5467         layerViewFrame.size.height = maxHeight;
5468         NSRect visibleRect = [[self enclosingScrollView] documentVisibleRect];
5469         
5470         // Place the top of the layer-hosting view at the top of the visibleRect.
5471         CGFloat topOffset = NSMinY(visibleRect);
5472         layerViewFrame.origin.y = topOffset;
5473
5474         // Compensate for the moved view by adjusting the sublayer transform on the view's layer (using flipped coords).
5475         CATransform3D flipTransform = CATransform3DMakeTranslation(0, topOffset, 0);
5476         flipTransform = CATransform3DScale(flipTransform, 1, -1, 1);
5477         [[_private->layerHostingView layer] setSublayerTransform:flipTransform];
5478     }
5479
5480     [_private->layerHostingView _updateLayerGeometryFromView];  // Workaround for <rdar://problem/7071636>
5481     [_private->layerHostingView setFrame:layerViewFrame];
5482 }
5483 #endif // defined(BUILDING_ON_LEOPARD)
5484
5485 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
5486 {
5487     if (_private) {
5488         ASSERT(!_private->drawingIntoLayer);
5489         _private->drawingIntoLayer = YES;
5490     }
5491
5492     [super drawLayer:layer inContext:ctx];
5493
5494     if (_private)
5495         _private->drawingIntoLayer = NO;
5496 }
5497
5498 - (BOOL)_web_isDrawingIntoLayer
5499 {
5500     return _private->drawingIntoLayer;
5501 }
5502
5503 #endif // USE(ACCELERATED_COMPOSITING)
5504
5505 @end
5506
5507 @implementation WebHTMLView (WebNSTextInputSupport)
5508
5509 - (NSArray *)validAttributesForMarkedText
5510 {
5511     static NSArray *validAttributes;
5512     if (!validAttributes) {
5513         validAttributes = [[NSArray alloc] initWithObjects:
5514             NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName,
5515             NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];
5516         // NSText also supports the following attributes, but it's
5517         // hard to tell which are really required for text input to
5518         // work well; I have not seen any input method make use of them yet.
5519         //     NSFontAttributeName, NSForegroundColorAttributeName,
5520         //     NSBackgroundColorAttributeName, NSLanguageAttributeName.
5521         CFRetain(validAttributes);
5522     }
5523     LOG(TextInput, "validAttributesForMarkedText -> (...)");
5524     return validAttributes;
5525 }
5526
5527 - (NSTextInputContext *)inputContext
5528 {
5529     return _private->exposeInputContext ? [super inputContext] : nil;
5530 }
5531
5532 - (NSAttributedString *)textStorage
5533 {
5534     if (!_private->exposeInputContext) {
5535         LOG(TextInput, "textStorage -> nil");
5536         return nil;
5537     }
5538     NSAttributedString *result = [self attributedSubstringFromRange:NSMakeRange(0, UINT_MAX)];
5539     
5540     LOG(TextInput, "textStorage -> \"%@\"", result ? [result string] : @"");
5541     
5542     // We have to return an empty string rather than null to prevent TSM from calling -string
5543     return result ? result : [[[NSAttributedString alloc] initWithString:@""] autorelease];
5544 }
5545
5546 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
5547 {
5548     [self _executeSavedKeypressCommands];
5549
5550     NSWindow *window = [self window];
5551     WebFrame *frame = [self _frame];
5552
5553     if (window)
5554         thePoint = [window convertScreenToBase:thePoint];
5555     thePoint = [self convertPoint:thePoint fromView:nil];
5556
5557     DOMRange *range = [frame _characterRangeAtPoint:thePoint];
5558     if (!range) {
5559         LOG(TextInput, "characterIndexForPoint:(%f, %f) -> NSNotFound", thePoint.x, thePoint.y);
5560         return NSNotFound;
5561     }
5562     
5563     unsigned result = [frame _convertDOMRangeToNSRange:range].location;
5564     LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
5565     return result;
5566 }
5567
5568 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
5569 {
5570     [self _executeSavedKeypressCommands];
5571
5572     WebFrame *frame = [self _frame];
5573     
5574     // Just to match NSTextView's behavior. Regression tests cannot detect this;
5575     // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
5576     // (type something; try ranges (1, -1) and (2, -1).
5577     if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
5578         theRange.length = 0;
5579     
5580     DOMRange *range = [frame _convertNSRangeToDOMRange:theRange];
5581     if (!range) {
5582         LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (0, 0, 0, 0)", theRange.location, theRange.length);
5583         return NSMakeRect(0, 0, 0, 0);
5584     }
5585     
5586     ASSERT([range startContainer]);
5587     ASSERT([range endContainer]);
5588     
5589     NSRect resultRect = [frame _firstRectForDOMRange:range];
5590     resultRect = [self convertRect:resultRect toView:nil];
5591
5592     NSWindow *window = [self window];
5593     if (window)
5594         resultRect.origin = [window convertBaseToScreen:resultRect.origin];
5595     
5596     LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
5597     return resultRect;
5598 }
5599
5600 - (NSRange)selectedRange
5601 {
5602     [self _executeSavedKeypressCommands];
5603
5604     if (!isTextInput(core([self _frame]))) {
5605         LOG(TextInput, "selectedRange -> (NSNotFound, 0)");
5606         return NSMakeRange(NSNotFound, 0);
5607     }
5608     NSRange result = [[self _frame] _selectedNSRange];
5609
5610     LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length);
5611     return result;
5612 }
5613
5614 - (NSRange)markedRange
5615 {
5616     [self _executeSavedKeypressCommands];
5617
5618     WebFrame *webFrame = [self _frame];
5619     Frame* coreFrame = core(webFrame);
5620     if (!coreFrame)
5621         return NSMakeRange(0, 0);
5622     NSRange result = [webFrame _convertToNSRange:coreFrame->editor()->compositionRange().get()];
5623
5624     LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
5625     return result;
5626 }
5627
5628 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange
5629 {
5630     [self _executeSavedKeypressCommands];
5631
5632     WebFrame *frame = [self _frame];
5633     Frame* coreFrame = core(frame);
5634     if (!isTextInput(coreFrame) || isInPasswordField(coreFrame)) {
5635         LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
5636         return nil;
5637     }
5638     RefPtr<Range> range = [frame _convertToDOMRange:nsRange];
5639     if (!range) {
5640         LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
5641         return nil;
5642     }
5643
5644     NSAttributedString *result = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
5645     
5646     // [WebHTMLConverter editingAttributedStringFromRange:]  insists on inserting a trailing 
5647     // whitespace at the end of the string which breaks the ATOK input method.  <rdar://problem/5400551>
5648     // To work around this we truncate the resultant string to the correct length.
5649     if ([result length] > nsRange.length) {
5650         ASSERT([result length] == nsRange.length + 1);
5651         ASSERT([[result string] characterAtIndex:nsRange.length] == '\n' || [[result string] characterAtIndex:nsRange.length] == ' ');
5652         result = [result attributedSubstringFromRange:NSMakeRange(0, nsRange.length)];
5653     }
5654     LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result string]);
5655     return result;
5656 }
5657
5658 - (NSInteger)conversationIdentifier
5659 {
5660     return (NSInteger)self;
5661 }
5662
5663 - (BOOL)hasMarkedText
5664 {
5665     Frame* coreFrame = core([self _frame]);
5666     BOOL result = coreFrame && coreFrame->editor()->hasComposition();
5667
5668     if (result) {
5669         // A saved command can confirm a composition, but it cannot start a new one.
5670         [self _executeSavedKeypressCommands];
5671         result = coreFrame->editor()->hasComposition();
5672     }
5673
5674     LOG(TextInput, "hasMarkedText -> %u", result);
5675     return result;
5676 }
5677
5678 - (void)unmarkText
5679 {
5680     [self _executeSavedKeypressCommands];
5681
5682     LOG(TextInput, "unmarkText");
5683
5684     // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
5685     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5686
5687     if (parameters) {
5688         parameters->eventInterpretationHadSideEffects = true;
5689         parameters->consumedByIM = false;
5690     }
5691     
5692     if (Frame* coreFrame = core([self _frame]))
5693         coreFrame->editor()->confirmComposition();
5694 }
5695
5696 static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
5697 {
5698     int length = [[string string] length];
5699
5700     int i = 0;
5701     while (i < length) {
5702         NSRange range;
5703         NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
5704
5705         if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
5706             Color color = Color::black;
5707             if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
5708                 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
5709             result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
5710         }
5711
5712         i = range.location + range.length;
5713     }
5714 }
5715
5716 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
5717 {
5718     [self _executeSavedKeypressCommands];
5719
5720     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
5721     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
5722
5723     LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length);
5724
5725     // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
5726     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5727
5728     if (parameters) {
5729         parameters->eventInterpretationHadSideEffects = true;
5730         parameters->consumedByIM = false;
5731     }
5732     
5733     Frame* coreFrame = core([self _frame]);
5734     if (!coreFrame)
5735         return;
5736
5737     if (![self _isEditable])
5738         return;
5739
5740     Vector<CompositionUnderline> underlines;
5741     NSString *text;
5742     NSRange replacementRange = { NSNotFound, 0 };
5743
5744     if (isAttributedString) {
5745         // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
5746         text = [string string];
5747         NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])];
5748         LOG(TextInput, "    ReplacementRange: %@", rangeString);
5749         // The AppKit adds a 'secret' property to the string that contains the replacement range.
5750         // The replacement range is the range of the the text that should be replaced with the new string.
5751         if (rangeString)
5752             replacementRange = NSRangeFromString(rangeString);
5753
5754         extractUnderlines(string, underlines);
5755     } else
5756         text = string;
5757
5758     if (replacementRange.location != NSNotFound)
5759         [[self _frame] _selectNSRange:replacementRange];
5760
5761     coreFrame->editor()->setComposition(text, underlines, newSelRange.location, NSMaxRange(newSelRange));
5762 }
5763
5764 - (void)doCommandBySelector:(SEL)selector
5765 {
5766     LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
5767
5768     // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
5769     // The same call to interpretKeyEvents can do more than one command.
5770     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5771     if (parameters)
5772         parameters->consumedByIM = false;
5773
5774     KeyboardEvent* event = parameters ? parameters->event : 0;
5775     bool shouldSaveCommand = parameters && parameters->shouldSaveCommands;
5776
5777     // As in insertText:, we assume that the call comes from an input method if there is marked text.
5778     RefPtr<Frame> coreFrame = core([self _frame]);
5779     bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition();
5780
5781     if (event && shouldSaveCommand && !isFromInputMethod)
5782         event->keypressCommands().append(KeypressCommand(NSStringFromSelector(selector)));
5783     else {
5784         // Make sure that only direct calls to doCommandBySelector: see the parameters by setting to 0.
5785         _private->interpretKeyEventsParameters = 0;
5786
5787         bool eventWasHandled;
5788
5789         WebView *webView = [self _webView];
5790         if ([[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector])
5791             eventWasHandled = true;
5792         else {
5793             Editor::Command command = [self coreCommandBySelector:selector];
5794             if (command.isSupported())
5795                 eventWasHandled = command.execute(event);
5796             else {
5797                 // If WebKit does not support this command, we need to pass the selector to super.
5798                 _private->selectorForDoCommandBySelector = selector;
5799
5800                 // The sink does two things: 1) Tells us if the responder went unhandled, and
5801                 // 2) prevents any NSBeep; we don't ever want to beep here.
5802                 WebResponderChainSink *sink = [[WebResponderChainSink alloc] initWithResponderChain:self];
5803                 [super doCommandBySelector:selector];
5804                 eventWasHandled = ![sink receivedUnhandledCommand];
5805                 [sink detach];
5806                 [sink release];
5807
5808                 _private->selectorForDoCommandBySelector = 0;
5809             }
5810         }
5811
5812         if (parameters)
5813             parameters->eventInterpretationHadSideEffects |= eventWasHandled;
5814
5815         _private->interpretKeyEventsParameters = parameters;
5816     }
5817 }
5818
5819 - (void)insertText:(id)string
5820 {
5821     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
5822     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
5823
5824     LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
5825
5826     WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters;
5827     if (parameters)
5828         parameters->consumedByIM = false;
5829
5830     RefPtr<Frame> coreFrame = core([self _frame]);
5831     NSString *text;
5832     NSRange replacementRange = { NSNotFound, 0 };
5833     bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition();
5834
5835     if (isAttributedString) {
5836         // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
5837         // It does not look like any input methods ever use insertText: with attributes other than NSTextInputReplacementRangeAttributeName.
5838         text = [string string];
5839         NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])];
5840         LOG(TextInput, "    ReplacementRange: %@", rangeString);
5841         if (rangeString) {
5842             replacementRange = NSRangeFromString(rangeString);
5843             isFromInputMethod = true;
5844         }
5845     } else
5846         text = string;
5847
5848     KeyboardEvent* event = parameters ? parameters->event : 0;
5849
5850     // insertText can be called for several reasons:
5851     // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later.
5852     // - If it's from an input method, then we should go ahead and insert the text now. We assume it's from the input method if we have marked text.
5853     // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method.
5854     // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
5855     // then we also execute it immediately, as there will be no other chance.
5856     bool shouldSaveCommand = parameters && parameters->shouldSaveCommands;
5857     if (event && shouldSaveCommand && !isFromInputMethod) {
5858         event->keypressCommands().append(KeypressCommand("insertText:", text));
5859         return;
5860     }
5861
5862     if (!coreFrame || !coreFrame->editor()->canEdit())
5863         return;
5864
5865     if (replacementRange.location != NSNotFound)
5866         [[self _frame] _selectNSRange:replacementRange];
5867
5868     bool eventHandled = false;
5869     String eventText = text;
5870     eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
5871     if (!coreFrame->editor()->hasComposition()) {
5872         // An insertText: might be handled by other responders in the chain if we don't handle it.
5873         // One example is space bar that results in scrolling down the page.
5874         eventHandled = coreFrame->editor()->insertText(eventText, event);
5875     } else {
5876         eventHandled = true;
5877         coreFrame->editor()->confirmComposition(eventText);
5878     }
5879     
5880     if (parameters)
5881         parameters->eventInterpretationHadSideEffects |= eventHandled;
5882 }
5883
5884 - (void)_updateSecureInputState
5885 {
5886     if (![[self window] isKeyWindow] || ([[self window] firstResponder] != self && !_private->_forceUpdateSecureInputState)) {
5887         if (_private->isInSecureInputState) {
5888             DisableSecureEventInput();
5889             _private->isInSecureInputState = NO;
5890         }
5891         return;
5892     }
5893
5894     Frame* coreFrame = core([self _frame]);
5895     if (!coreFrame)
5896         return;
5897
5898     if (isInPasswordField(coreFrame)) {
5899         if (!_private->isInSecureInputState)
5900             EnableSecureEventInput();
5901         _private->isInSecureInputState = YES;
5902         // WebKit substitutes nil for input context when in password field, which corresponds to null TSMDocument. So, there is
5903         // no need to call TSMGetActiveDocument(), which may return an incorrect result when selection hasn't been yet updated
5904         // after focusing a node.
5905         static CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
5906         TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag, sizeof(CFArrayRef), &inputSources);
5907     } else {
5908         if (_private->isInSecureInputState)
5909             DisableSecureEventInput();
5910         _private->isInSecureInputState = NO;
5911         TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
5912     }
5913 }
5914
5915 - (void)_updateSelectionForInputManager
5916 {
5917     Frame* coreFrame = core([self _frame]);
5918     if (!coreFrame)
5919         return;
5920
5921     BOOL exposeInputContext = isTextInput(coreFrame) && !isInPasswordField(coreFrame);
5922     if (exposeInputContext != _private->exposeInputContext) {
5923         _private->exposeInputContext = exposeInputContext;
5924         // Let AppKit cache a potentially changed input context.
5925         // WebCore routinely sets the selection to None when editing, and IMs become unhappy when an input context suddenly turns nil, see bug 26009.
5926         if (!coreFrame->selection()->isNone())
5927             [NSApp updateWindows];
5928     }
5929
5930     [self _updateSecureInputState];
5931
5932     if (!coreFrame->editor()->hasComposition())
5933         return;
5934
5935     if (coreFrame->editor()->ignoreCompositionSelectionChange())
5936         return;
5937
5938     unsigned start;
5939     unsigned end;
5940     if (coreFrame->editor()->getCompositionSelection(start, end))
5941         [[NSInputManager currentInputManager] markedTextSelectionChanged:NSMakeRange(start, end - start) client:self];
5942     else {
5943         coreFrame->editor()->cancelComposition();
5944         [[NSInputManager currentInputManager] markedTextAbandoned:self];
5945     }
5946 }
5947
5948 @end
5949
5950 @implementation WebHTMLView (WebDocumentPrivateProtocols)
5951
5952 - (NSRect)selectionRect
5953 {
5954     if (![self _hasSelection])
5955         return NSZeroRect;
5956     return core([self _frame])->selection()->bounds();
5957 }
5958
5959 - (NSArray *)selectionTextRects
5960 {
5961     if (![self _hasSelection])
5962         return nil;
5963
5964     Vector<FloatRect> list;
5965     if (Frame* coreFrame = core([self _frame]))
5966         coreFrame->selection()->getClippedVisibleTextRectangles(list);
5967
5968     size_t size = list.size();
5969
5970     NSMutableArray *result = [NSMutableArray arrayWithCapacity:size];
5971
5972     for (size_t i = 0; i < size; ++i)
5973         [result addObject:[NSValue valueWithRect:list[i]]];
5974
5975     return result;
5976 }
5977
5978 - (NSView *)selectionView
5979 {
5980     return self;
5981 }
5982
5983 - (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText
5984 {
5985     if (![self _hasSelection])
5986         return nil;
5987     return core([self _frame])->selectionImage(forceBlackText);
5988 }
5989
5990 - (NSRect)selectionImageRect
5991 {
5992     if (![self _hasSelection])
5993         return NSZeroRect;
5994     return core([self _frame])->selection()->bounds();
5995 }
5996
5997 - (NSArray *)pasteboardTypesForSelection
5998 {
5999     if ([self _canSmartCopyOrDelete]) {
6000         NSMutableArray *types = [[[[self class] _selectionPasteboardTypes] mutableCopy] autorelease];
6001         [types addObject:WebSmartPastePboardType];
6002         return types;
6003     } else {
6004         return [[self class] _selectionPasteboardTypes];
6005     }
6006 }
6007
6008 - (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
6009 {
6010     [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:nil];
6011 }
6012
6013 - (void)selectAll
6014 {
6015     Frame* coreFrame = core([self _frame]);
6016     if (coreFrame)
6017         coreFrame->selection()->selectAll();
6018 }
6019
6020 - (void)deselectAll
6021 {
6022     Frame* coreFrame = core([self _frame]);
6023     if (!coreFrame)
6024         return;
6025     coreFrame->selection()->clear();
6026 }
6027
6028 - (NSString *)string
6029 {
6030     return [[self _frame] _stringForRange:[self _documentRange]];
6031 }
6032
6033 - (NSAttributedString *)_attributeStringFromDOMRange:(DOMRange *)range
6034 {
6035     NSAttributedString *attributedString;
6036 #if !LOG_DISABLED        
6037     double start = CFAbsoluteTimeGetCurrent();
6038 #endif    
6039     attributedString = [[[NSAttributedString alloc] _initWithDOMRange:range] autorelease];
6040 #if !LOG_DISABLED
6041     double duration = CFAbsoluteTimeGetCurrent() - start;
6042     LOG(Timing, "creating attributed string from selection took %f seconds.", duration);
6043 #endif
6044     return attributedString;
6045 }
6046
6047 - (NSAttributedString *)attributedString
6048 {
6049     DOMDocument *document = [[self _frame] DOMDocument];
6050     NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[document _documentRange]];
6051     if (!attributedString) {
6052         Document* coreDocument = core(document);
6053         attributedString = [WebHTMLConverter editingAttributedStringFromRange:Range::create(coreDocument, coreDocument, 0, coreDocument, coreDocument->childNodeCount()).get()];
6054     }
6055     return attributedString;
6056 }
6057
6058 - (NSString *)selectedString
6059 {
6060     return [[self _frame] _selectedString];
6061 }
6062
6063 - (NSAttributedString *)selectedAttributedString
6064 {
6065     NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[self _selectedRange]];
6066     if (!attributedString) {
6067         Frame* coreFrame = core([self _frame]);
6068         if (coreFrame) {
6069             RefPtr<Range> range = coreFrame->selection()->selection().toNormalizedRange();
6070             attributedString = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
6071         }
6072     }
6073     return attributedString;
6074 }
6075
6076 - (BOOL)supportsTextEncoding
6077 {
6078     return YES;
6079 }
6080
6081 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
6082 {
6083     return [self _findString:string options:(forward ? 0 : WebFindOptionsBackwards) | (caseFlag ? 0 : WebFindOptionsCaseInsensitive) | (wrapFlag ? WebFindOptionsWrapAround : 0) | (startInSelection ? WebFindOptionsStartInSelection : 0)];
6084 }
6085
6086 @end
6087
6088 @implementation WebHTMLView (WebDocumentInternalProtocols)
6089
6090 - (NSDictionary *)elementAtPoint:(NSPoint)point
6091 {
6092     return [self elementAtPoint:point allowShadowContent:NO];
6093 }
6094
6095 - (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow
6096 {
6097     Frame* coreFrame = core([self _frame]);
6098     if (!coreFrame)
6099         return nil;
6100     return [[[WebElementDictionary alloc] initWithHitTestResult:coreFrame->eventHandler()->hitTestResultAtPoint(IntPoint(point), allow)] autorelease];
6101 }
6102
6103 - (NSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options limit:(NSUInteger)limit markMatches:(BOOL)markMatches
6104 {
6105     Frame* coreFrame = core([self _frame]);
6106     if (!coreFrame)
6107         return 0;
6108
6109     return coreFrame->editor()->countMatchesForText(string, core(range), coreOptions(options), limit, markMatches);
6110 }
6111
6112 - (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
6113 {
6114     Frame* coreFrame = core([self _frame]);
6115     if (!coreFrame)
6116         return;
6117     coreFrame->editor()->setMarkedTextMatchesAreHighlighted(newValue);
6118 }
6119
6120 - (BOOL)markedTextMatchesAreHighlighted
6121 {
6122     Frame* coreFrame = core([self _frame]);
6123     return coreFrame && coreFrame->editor()->markedTextMatchesAreHighlighted();
6124 }
6125
6126 - (void)unmarkAllTextMatches
6127 {
6128     Frame* coreFrame = core([self _frame]);
6129     if (!coreFrame)
6130         return;
6131     Document* document = coreFrame->document();
6132     if (!document)
6133         return;
6134     document->markers()->removeMarkers(DocumentMarker::TextMatch);
6135 }
6136
6137 - (NSArray *)rectsForTextMatches
6138 {
6139     Frame* coreFrame = core([self _frame]);
6140     if (!coreFrame)
6141         return [NSArray array];
6142     Document* document = coreFrame->document();
6143     if (!document)
6144         return [NSArray array];
6145
6146     Vector<IntRect> rects = document->markers()->renderedRectsForMarkers(DocumentMarker::TextMatch);
6147     unsigned count = rects.size();
6148     NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
6149     for (unsigned index = 0; index < count; ++index)
6150         [result addObject:[NSValue valueWithRect:rects[index]]];    
6151     return result;
6152 }
6153
6154 - (BOOL)_findString:(NSString *)string options:(WebFindOptions)options
6155 {
6156     if (![string length])
6157         return NO;
6158     Frame* coreFrame = core([self _frame]);
6159     return coreFrame && coreFrame->editor()->findString(string, coreOptions(options));
6160 }
6161
6162 @end
6163
6164 // This is used by AppKit and is included here so that WebDataProtocolScheme is only defined once.
6165 @implementation NSURL (WebDataURL)
6166
6167 + (NSURL *)_web_uniqueWebDataURL
6168 {
6169     CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
6170     NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
6171     CFRelease(UUIDRef);
6172     NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", WebDataProtocolScheme, UUIDString]];
6173     CFRelease(UUIDString);
6174     return URL;
6175 }
6176
6177 @end
6178
6179 @implementation WebResponderChainSink
6180
6181 - (id)initWithResponderChain:(NSResponder *)chain
6182 {
6183     self = [super init];
6184     _lastResponderInChain = chain;
6185     while (NSResponder *next = [_lastResponderInChain nextResponder])
6186         _lastResponderInChain = next;
6187     [_lastResponderInChain setNextResponder:self];
6188     return self;
6189 }
6190
6191 - (void)detach
6192 {
6193     [_lastResponderInChain setNextResponder:nil];
6194     _lastResponderInChain = nil;
6195 }
6196
6197 - (BOOL)receivedUnhandledCommand
6198 {
6199     return _receivedUnhandledCommand;
6200 }
6201
6202 - (void)noResponderFor:(SEL)selector
6203 {
6204     _receivedUnhandledCommand = YES;
6205 }
6206
6207 - (void)doCommandBySelector:(SEL)selector
6208 {
6209     _receivedUnhandledCommand = YES;
6210 }
6211
6212 - (BOOL)tryToPerform:(SEL)action with:(id)object
6213 {
6214     _receivedUnhandledCommand = YES;
6215     return YES;
6216 }
6217
6218 @end