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