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