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