Invoke the context menu delegate callback right before we return the menu
[WebKit-https.git] / Source / WebKit / mac / WebView / WebHTMLView.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
3  *           (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #import "WebHTMLView.h"
31
32 #import "DOMCSSStyleDeclarationInternal.h"
33 #import "DOMDocumentFragmentInternal.h"
34 #import "DOMDocumentInternal.h"
35 #import "DOMNodeInternal.h"
36 #import "DOMRangeInternal.h"
37 #import "WebArchive.h"
38 #import "WebClipView.h"
39 #import "WebContextMenuClient.h"
40 #import "WebDOMOperationsInternal.h"
41 #import "WebDataSourceInternal.h"
42 #import "WebDefaultUIDelegate.h"
43 #import "WebDelegateImplementationCaching.h"
44 #import "WebDocumentInternal.h"
45 #import "WebDynamicScrollBarsViewInternal.h"
46 #import "WebEditingDelegate.h"
47 #import "WebElementDictionary.h"
48 #import "WebFrameInternal.h"
49 #import "WebFramePrivate.h"
50 #import "WebFrameViewInternal.h"
51 #import "WebHTMLRepresentationPrivate.h"
52 #import "WebHTMLViewInternal.h"
53 #import "WebImmediateActionController.h"
54 #import "WebKitLogging.h"
55 #import "WebKitNSStringExtras.h"
56 #import "WebKitVersionChecks.h"
57 #import "WebLocalizableStringsInternal.h"
58 #import "WebNSFileManagerExtras.h"
59 #import "WebNSImageExtras.h"
60 #import "WebNSObjectExtras.h"
61 #import "WebNSPrintOperationExtras.h"
62 #import "WebNSURLExtras.h"
63 #import "WebNSViewExtras.h"
64 #import "WebNetscapePluginView.h"
65 #import "WebNodeHighlight.h"
66 #import "WebPluginController.h"
67 #import "WebPreferences.h"
68 #import "WebPreferencesPrivate.h"
69 #import "WebResourcePrivate.h"
70 #import "WebSharingServicePickerController.h"
71 #import "WebTextCompletionController.h"
72 #import "WebTypesInternal.h"
73 #import "WebUIDelegatePrivate.h"
74 #import "WebViewInternal.h"
75 #import <WebCore/BlockExceptions.h>
76 #import <WebCore/CSSStyleDeclaration.h>
77 #import <WebCore/CachedImage.h>
78 #import <WebCore/CachedResourceClient.h>
79 #import <WebCore/CachedResourceLoader.h>
80 #import <WebCore/Chrome.h>
81 #import <WebCore/ColorMac.h>
82 #import <WebCore/ContextMenu.h>
83 #import <WebCore/ContextMenuController.h>
84 #import <WebCore/DictionaryLookup.h>
85 #import <WebCore/Document.h>
86 #import <WebCore/DocumentFragment.h>
87 #import <WebCore/DocumentMarkerController.h>
88 #import <WebCore/DragController.h>
89 #import <WebCore/DragImage.h>
90 #import <WebCore/Editor.h>
91 #import <WebCore/EditorDeleteAction.h>
92 #import <WebCore/Element.h>
93 #import <WebCore/EventHandler.h>
94 #import <WebCore/ExceptionHandlers.h>
95 #import <WebCore/FloatRect.h>
96 #import <WebCore/FocusController.h>
97 #import <WebCore/Font.h>
98 #import <WebCore/FontCache.h>
99 #import <WebCore/Frame.h>
100 #import <WebCore/FrameLoader.h>
101 #import <WebCore/FrameSelection.h>
102 #import <WebCore/FrameView.h>
103 #import <WebCore/HTMLConverter.h>
104 #import <WebCore/HTMLNames.h>
105 #import <WebCore/HitTestResult.h>
106 #import <WebCore/Image.h>
107 #import <WebCore/KeyboardEvent.h>
108 #import <WebCore/LegacyWebArchive.h>
109 #import <WebCore/LocalizedStrings.h>
110 #import <WebCore/MIMETypeRegistry.h>
111 #import <WebCore/MainFrame.h>
112 #import <WebCore/NSURLFileTypeMappingsSPI.h>
113 #import <WebCore/Page.h>
114 #import <WebCore/Range.h>
115 #import <WebCore/RenderView.h>
116 #import <WebCore/RenderWidget.h>
117 #import <WebCore/RuntimeApplicationChecks.h>
118 #import <WebCore/SharedBuffer.h>
119 #import <WebCore/StyleProperties.h>
120 #import <WebCore/Text.h>
121 #import <WebCore/TextAlternativeWithRange.h>
122 #import <WebCore/TextIndicator.h>
123 #import <WebCore/TextUndoInsertionMarkupMac.h>
124 #import <WebCore/WebCoreObjCExtras.h>
125 #import <WebCore/WebNSAttributedStringExtras.h>
126 #import <WebCore/markup.h>
127 #import <WebKitLegacy/DOM.h>
128 #import <WebKitLegacy/DOMExtensions.h>
129 #import <WebKitLegacy/DOMPrivate.h>
130 #import <WebKitSystemInterface.h>
131 #import <dlfcn.h>
132 #import <limits>
133 #import <runtime/InitializeThreading.h>
134 #import <wtf/MainThread.h>
135 #import <wtf/MathExtras.h>
136 #import <wtf/ObjcRuntimeExtras.h>
137 #import <wtf/RunLoop.h>
138
139 #if !PLATFORM(IOS)
140 #import <AppKit/NSAccessibility.h>
141 #import <ApplicationServices/ApplicationServices.h>
142 #import <WebCore/NSMenuSPI.h>
143 #import <WebCore/PlatformEventFactoryMac.h>
144 #import "WebNSEventExtras.h"
145 #import "WebNSPasteboardExtras.h"
146 #endif
147
148 #import <QuartzCore/QuartzCore.h>
149
150 #if PLATFORM(IOS)
151 #import "WebUIKitDelegate.h"
152 #import <WebCore/KeyEventCodesIOS.h>
153 #import <WebCore/PlatformEventFactoryIOS.h>
154 #import <WebCore/WAKClipView.h>
155 #import <WebCore/WAKScrollView.h>
156 #import <WebCore/WAKWindow.h>
157 #import <WebCore/WKGraphics.h>
158 #import <WebCore/WebEvent.h>
159 #endif
160
161 using namespace WebCore;
162 using namespace HTMLNames;
163 using namespace WTF;
164
165 #if PLATFORM(IOS)
166 @interface NSObject (Accessibility)
167 - (id)accessibilityHitTest:(NSPoint)point;
168 - (id)accessibilityFocusedUIElement;
169 @end
170 #endif
171
172 #if !PLATFORM(IOS)
173 @interface WebMenuTarget : NSObject {
174     WebCore::ContextMenuController* _menuController;
175 }
176 + (WebMenuTarget*)sharedMenuTarget;
177 - (WebCore::ContextMenuController*)menuController;
178 - (void)setMenuController:(WebCore::ContextMenuController*)menuController;
179 - (void)forwardContextMenuAction:(id)sender;
180 - (BOOL)validateMenuItem:(NSMenuItem *)item;
181 @end
182
183 static WebMenuTarget* target;
184
185 @implementation WebMenuTarget
186
187 + (WebMenuTarget*)sharedMenuTarget
188 {
189     if (!target)
190         target = [[WebMenuTarget alloc] init];
191     return target;
192 }
193
194 - (WebCore::ContextMenuController*)menuController
195 {
196     return _menuController;
197 }
198
199 - (void)setMenuController:(WebCore::ContextMenuController*)menuController
200 {
201     _menuController = menuController;
202 }
203
204 - (void)forwardContextMenuAction:(id)sender
205 {
206     WebCore::ContextMenuItem item(WebCore::ActionType, static_cast<WebCore::ContextMenuAction>([sender tag]), [sender title]);
207     _menuController->contextMenuItemSelected(&item);
208 }
209
210 - (BOOL)validateMenuItem:(NSMenuItem *)item
211 {
212     WebCore::ContextMenuItem coreItem(item);
213     ASSERT(_menuController->contextMenu());
214     _menuController->checkOrEnableIfNeeded(coreItem);
215     return coreItem.enabled();
216 }
217
218 @end
219
220 @interface NSWindow (BorderViewAccess)
221 - (NSView*)_web_borderView;
222 @end
223
224 @implementation NSWindow (BorderViewAccess)
225 - (NSView*)_web_borderView
226 {
227     return _borderView;
228 }
229 @end
230
231 @interface WebResponderChainSink : NSResponder {
232     NSResponder* _lastResponderInChain;
233     BOOL _receivedUnhandledCommand;
234 }
235 - (id)initWithResponderChain:(NSResponder *)chain;
236 - (void)detach;
237 - (BOOL)receivedUnhandledCommand;
238 @end
239
240 @interface WebLayerHostingFlippedView : NSView
241 @end
242
243 @implementation WebLayerHostingFlippedView
244 - (BOOL)isFlipped
245 {
246     return YES;
247 }
248 @end
249
250 @interface WebRootLayer : CALayer
251 @end
252
253 @implementation WebRootLayer
254 - (void)renderInContext:(CGContextRef)ctx
255 {
256     // AppKit calls -[CALayer renderInContext:] to render layer-backed views
257     // into bitmap contexts, but renderInContext: doesn't capture mask layers
258     // (<rdar://problem/9539526>), so we can't rely on it. Since our layer
259     // contents will have already been rendered by drawRect:, we can safely make
260     // this a NOOP.
261 }
262 @end
263
264 // if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view)
265 static BOOL forceNSViewHitTest;
266
267 // 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])
268 static BOOL forceWebHTMLViewHitTest;
269 #endif // !PLATFORM(IOS)
270
271 static WebHTMLView *lastHitView;
272
273 #if !PLATFORM(IOS)
274 static bool needsCursorRectsSupportAtPoint(NSWindow* window, NSPoint point)
275 {
276     forceNSViewHitTest = YES;
277     NSView* view = [[window _web_borderView] hitTest:point];
278     forceNSViewHitTest = NO;
279
280     // WebHTMLView doesn't use cursor rects.
281     if ([view isKindOfClass:[WebHTMLView class]])
282         return false;
283
284 #if ENABLE(NETSCAPE_PLUGIN_API)
285     // Neither do NPAPI plug-ins.
286     if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
287         return false;
288 #endif
289
290     // Non-Web content, WebPDFView, and WebKit plug-ins use normal cursor handling.
291     return true;
292 }
293
294
295 static IMP oldSetCursorForMouseLocationIMP;
296
297 // Overriding an internal method is a hack; <rdar://problem/7662987> tracks finding a better solution.
298 static void setCursor(NSWindow *self, SEL cmd, NSPoint point)
299 {
300     if (needsCursorRectsSupportAtPoint(self, point))
301         wtfCallIMP<id>(oldSetCursorForMouseLocationIMP, self, cmd, point);
302 }
303
304 #endif
305
306 extern "C" {
307
308 // Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers.
309
310 extern NSString *NSMarkedClauseSegmentAttributeName;
311 extern NSString *NSTextInputReplacementRangeAttributeName;
312
313 }
314
315 @interface NSView (WebNSViewDetails)
316 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView;
317 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect;
318 #if !PLATFORM(IOS)
319 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView;
320 - (void)_recursive:(BOOL)recurseX displayRectIgnoringOpacity:(NSRect)displayRect inGraphicsContext:(NSGraphicsContext *)graphicsContext CGContext:(CGContextRef)ctx topView:(BOOL)isTopView shouldChangeFontReferenceColor:(BOOL)shouldChangeFontReferenceColor;
321 #endif
322 - (NSRect)_dirtyRect;
323 - (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants;
324 - (BOOL)_drawnByAncestor;
325 - (void)_invalidateGStatesForTree;
326 - (void)_propagateDirtyRectsToOpaqueAncestors;
327 - (void)_windowChangedKeyState;
328 #if PLATFORM(IOS)
329 - (void)centerSelectionInVisibleArea:(id)sender;
330 #endif
331 @end
332
333 #if !PLATFORM(IOS)
334 static IMP oldSetNeedsDisplayInRectIMP;
335
336 static void setNeedsDisplayInRect(NSView *self, SEL cmd, NSRect invalidRect)
337 {
338     if (![NSThread isMainThread] || ![self _drawnByAncestor]) {
339         wtfCallIMP<id>(oldSetNeedsDisplayInRectIMP, self, cmd, invalidRect);
340         return;
341     }
342
343     static Class webFrameViewClass = [WebFrameView class];
344     WebFrameView *enclosingWebFrameView = (WebFrameView *)self;
345     while (enclosingWebFrameView && ![enclosingWebFrameView isKindOfClass:webFrameViewClass])
346         enclosingWebFrameView = (WebFrameView *)[enclosingWebFrameView superview];
347
348     if (!enclosingWebFrameView) {
349         wtfCallIMP<id>(oldSetNeedsDisplayInRectIMP, self, cmd, invalidRect);
350         return;
351     }
352
353     Frame* coreFrame = core([enclosingWebFrameView webFrame]);
354     FrameView* frameView = coreFrame ? coreFrame->view() : 0;
355     if (!frameView || !frameView->isEnclosedInCompositingLayer()) {
356         wtfCallIMP<id>(oldSetNeedsDisplayInRectIMP, self, cmd, invalidRect);
357         return;
358     }
359
360     NSRect invalidRectInWebFrameViewCoordinates = [enclosingWebFrameView convertRect:invalidRect fromView:self];
361     IntRect invalidRectInFrameViewCoordinates(invalidRectInWebFrameViewCoordinates);
362     if (![enclosingWebFrameView isFlipped])
363         invalidRectInFrameViewCoordinates.setY(frameView->frameRect().size().height() - invalidRectInFrameViewCoordinates.maxY());
364
365     frameView->invalidateRect(invalidRectInFrameViewCoordinates);
366 }
367
368 @interface NSApplication (WebNSApplicationDetails)
369 - (void)speakString:(NSString *)string;
370 @end
371
372 @interface NSWindow (WebNSWindowDetails)
373 - (id)_newFirstResponderAfterResigning;
374 @end
375
376 @interface NSAttributedString (WebNSAttributedStringDetails)
377 - (id)_initWithDOMRange:(DOMRange *)range;
378 - (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
379 @end
380
381 @interface NSSpellChecker (WebNSSpellCheckerDetails)
382 - (void)learnWord:(NSString *)word;
383 @end
384 #endif // !PLATFORM(IOS)
385
386 // By imaging to a width a little wider than the available pixels,
387 // thin pages will be scaled down a little, matching the way they
388 // print in IE and Camino. This lets them use fewer sheets than they
389 // would otherwise, which is presumably why other browsers do this.
390 // Wide pages will be scaled down more than this.
391 const float _WebHTMLViewPrintingMinimumShrinkFactor = 1.25;
392
393 // This number determines how small we are willing to reduce the page content
394 // in order to accommodate the widest line. If the page would have to be
395 // reduced smaller to make the widest line fit, we just clip instead (this
396 // behavior matches MacIE and Mozilla, at least)
397 const float _WebHTMLViewPrintingMaximumShrinkFactor = 2;
398
399 #define AUTOSCROLL_INTERVAL             0.1f
400
401 // Any non-zero value will do, but using something recognizable might help us debug some day.
402 #define TRACKING_RECT_TAG 0xBADFACE
403
404 // FIXME: This constant is copied from AppKit's _NXSmartPaste constant.
405 #define WebSmartPastePboardType @"NeXT smart paste pasteboard type"
406
407 #define STANDARD_WEIGHT 5
408 #define MIN_BOLD_WEIGHT 7
409 #define STANDARD_BOLD_WEIGHT 9
410
411 // Fake URL scheme.
412 #define WebDataProtocolScheme @"webkit-fake-url"
413
414 // <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present
415 // in some NIB files, so NSUnarchiver must be still able to look up this now-unused class.
416 @interface WebCoreScrollView : NSScrollView
417 @end
418
419 @implementation WebCoreScrollView
420 @end
421
422 #if !PLATFORM(IOS)
423 // We need this to be able to safely reference the CachedImage for the promised drag data
424 static CachedImageClient* promisedDataClient()
425 {
426     static CachedImageClient* staticCachedResourceClient = new CachedImageClient;
427     return staticCachedResourceClient;
428 }
429 #endif
430
431 #if PLATFORM(IOS)
432 static NSString * const WebMarkedTextUpdatedNotification = @"WebMarkedTextUpdated";
433 #endif
434
435 @interface WebHTMLView (WebHTMLViewFileInternal)
436 #if !PLATFORM(IOS)
437 - (BOOL)_imageExistsAtPaths:(NSArray *)paths;
438 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText;
439 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard;
440 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText;
441 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard;
442 - (void)_postFakeMouseMovedEventForFlagsChangedEvent:(NSEvent *)flagsChangedEvent;
443 - (void)_removeSuperviewObservers;
444 - (void)_removeWindowObservers;
445 #endif
446 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
447 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action;
448 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action;
449 - (DOMRange *)_selectedRange;
450 - (BOOL)_shouldDeleteRange:(DOMRange *)range;
451 #if !PLATFORM(IOS)
452 - (NSView *)_hitViewForEvent:(NSEvent *)event;
453 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString;
454 #endif
455 - (DOMRange *)_documentRange;
456 #if !PLATFORM(IOS)
457 - (void)_setMouseDownEvent:(NSEvent *)event;
458 #else
459 - (void)_setMouseDownEvent:(WebEvent *)event;
460 #endif
461 - (WebHTMLView *)_topHTMLView;
462 - (BOOL)_isTopHTMLView;
463 #if !PLATFORM(IOS)
464 - (void)_web_setPrintingModeRecursive;
465 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
466 - (void)_web_clearPrintingModeRecursive;
467 #endif
468 @end
469
470 @interface WebHTMLView (WebHTMLViewTextCheckingInternal)
471 - (void)orderFrontSubstitutionsPanel:(id)sender;
472 - (BOOL)smartInsertDeleteEnabled;
473 - (void)setSmartInsertDeleteEnabled:(BOOL)flag;
474 - (void)toggleSmartInsertDelete:(id)sender;
475 - (BOOL)isAutomaticQuoteSubstitutionEnabled;
476 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag;
477 - (void)toggleAutomaticQuoteSubstitution:(id)sender;
478 - (BOOL)isAutomaticLinkDetectionEnabled;
479 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag;
480 - (void)toggleAutomaticLinkDetection:(id)sender;
481 - (BOOL)isAutomaticDashSubstitutionEnabled;
482 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag;
483 - (void)toggleAutomaticDashSubstitution:(id)sender;
484 - (BOOL)isAutomaticTextReplacementEnabled;
485 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag;
486 - (void)toggleAutomaticTextReplacement:(id)sender;
487 - (BOOL)isAutomaticSpellingCorrectionEnabled;
488 - (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag;
489 - (void)toggleAutomaticSpellingCorrection:(id)sender;
490 @end
491
492 @interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
493 - (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageWidth logicalHeight:(float)minPageHeight originalPageWidth:(float)pageLogicalWidth originalPageHeight:(float)pageLogicalHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent;
494 #if !PLATFORM(IOS)
495 - (void)_updateSecureInputState;
496 #endif
497 @end
498
499 #if !PLATFORM(IOS)
500 @class NSTextInputContext;
501 @interface NSResponder (AppKitDetails)
502 - (NSTextInputContext *)inputContext;
503 @end
504
505 @interface NSObject (NSTextInputContextDetails)
506 - (BOOL)wantsToHandleMouseEvents;
507 - (BOOL)handleMouseEvent:(NSEvent *)event;
508 @end
509 #endif
510
511 #if !PLATFORM(IOS)
512 @interface WebHTMLView (WebNSTextInputSupport) <NSTextInput>
513 #else
514 @interface WebHTMLView (WebNSTextInputSupport)
515 #endif
516 - (void)_updateSelectionForInputManager;
517 #if PLATFORM(IOS)
518 - (void)doCommandBySelector:(SEL)selector;
519 #endif
520 @end
521
522 @interface WebHTMLView (WebEditingStyleSupport)
523 - (DOMCSSStyleDeclaration *)_emptyStyle;
524 #if !PLATFORM(IOS)
525 - (NSString *)_colorAsString:(NSColor *)color;
526 #endif
527 @end
528
529 @interface NSView (WebHTMLViewFileInternal)
530 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
531 @end
532
533 @interface NSMutableDictionary (WebHTMLViewFileInternal)
534 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key;
535 @end
536
537 struct WebHTMLViewInterpretKeyEventsParameters {
538     KeyboardEvent* event;
539     bool eventInterpretationHadSideEffects;
540     bool shouldSaveCommands;
541     bool consumedByIM;
542     bool executingSavedKeypressCommands;
543 };
544
545 @interface WebHTMLViewPrivate : NSObject {
546 @public
547     BOOL closed;
548     BOOL ignoringMouseDraggedEvents;
549     BOOL printing;
550     BOOL paginateScreenContent;
551 #if !PLATFORM(IOS)
552     BOOL observingSuperviewNotifications;
553     BOOL observingWindowNotifications;
554     
555     id savedSubviews;
556     BOOL subviewsSetAside;
557 #endif
558
559     NSView *layerHostingView;
560     BOOL drawingIntoLayer;
561
562 #if !PLATFORM(IOS)
563     NSEvent *mouseDownEvent; // Kept after handling the event.
564 #else
565     WebEvent *mouseDownEvent; // Kept after handling the event.
566 #endif
567     BOOL handlingMouseDownEvent;
568 #if !PLATFORM(IOS)    
569     NSEvent *keyDownEvent; // Kept after handling the event.
570 #else
571     WebEvent *keyDownEvent; // Kept after handling the event.
572 #endif
573
574     // A WebHTMLView has a single input context, but we return nil when in non-editable content to avoid making input methods do their work.
575     // This state is saved each time selection changes, because computing it causes style recalc, which is not always safe to do.
576     BOOL exposeInputContext;
577
578 #if !PLATFORM(IOS)
579     // Track whether the view has set a secure input state.
580     BOOL isInSecureInputState;
581
582     BOOL _forceUpdateSecureInputState;
583 #endif
584
585     NSPoint lastScrollPosition;
586     BOOL inScrollPositionChanged;
587
588     WebPluginController *pluginController;
589     
590     NSString *toolTip;
591 #if !PLATFORM(IOS)
592     NSToolTipTag lastToolTipTag;
593 #endif
594     id trackingRectOwner;
595     void *trackingRectUserData;
596     
597     NSTimer *autoscrollTimer;
598 #if !PLATFORM(IOS)    
599     NSEvent *autoscrollTriggerEvent;
600 #else
601     WebEvent *autoscrollTriggerEvent;
602 #endif
603     
604     NSArray *pageRects;
605
606 #if !PLATFORM(IOS)
607     WebTextCompletionController *completionController;
608 #endif
609     
610     BOOL transparentBackground;
611
612     WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters;
613     
614     WebDataSource *dataSource;
615 #if !PLATFORM(IOS)
616     WebCore::CachedImage* promisedDragTIFFDataSource;
617 #endif
618
619     SEL selectorForDoCommandBySelector;
620
621 #if !PLATFORM(IOS)
622     BOOL installedTrackingArea;
623     id flagsChangedEventMonitor;
624 #endif
625
626 #ifndef NDEBUG
627     BOOL enumeratingSubviews;
628 #endif
629
630 #if ENABLE(SERVICE_CONTROLS)
631     RetainPtr<WebSharingServicePickerController> currentSharingServicePickerController;
632 #endif
633 }
634 - (void)clear;
635 @end
636
637 #if !PLATFORM(IOS)
638 static NSCellStateValue kit(TriState state)
639 {
640     switch (state) {
641         case FalseTriState:
642             return NSOffState;
643         case TrueTriState:
644             return NSOnState;
645         case MixedTriState:
646             return NSMixedState;
647     }
648     ASSERT_NOT_REACHED();
649     return NSOffState;
650 }
651 #endif
652
653 @implementation WebHTMLViewPrivate
654
655 + (void)initialize
656 {
657 #if !PLATFORM(IOS)
658     JSC::initializeThreading();
659     WTF::initializeMainThreadToProcessMainThread();
660     RunLoop::initializeMainRunLoop();
661 #endif
662     WebCoreObjCFinalizeOnMainThread(self);
663     
664 #if !PLATFORM(IOS)
665     if (!oldSetCursorForMouseLocationIMP) {
666         Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:));
667         ASSERT(setCursorMethod);
668         oldSetCursorForMouseLocationIMP = method_setImplementation(setCursorMethod, (IMP)setCursor);
669         ASSERT(oldSetCursorForMouseLocationIMP);
670     }
671
672     if (!oldSetNeedsDisplayInRectIMP) {
673         Method setNeedsDisplayInRectMethod = class_getInstanceMethod([NSView class], @selector(setNeedsDisplayInRect:));
674         ASSERT(setNeedsDisplayInRectMethod);
675         oldSetNeedsDisplayInRectIMP = method_setImplementation(setNeedsDisplayInRectMethod, (IMP)setNeedsDisplayInRect);
676         ASSERT(oldSetNeedsDisplayInRectIMP);
677     }
678
679 #endif
680
681 }
682
683 - (void)dealloc
684 {
685     if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self))
686         return;
687
688     ASSERT(!autoscrollTimer);
689     ASSERT(!autoscrollTriggerEvent);
690     
691 #if !PLATFORM(IOS)
692     [mouseDownEvent release];
693     [keyDownEvent release];
694 #else
695     if (mouseDownEvent)
696         CFRelease (mouseDownEvent);
697     if (keyDownEvent)
698         CFRelease(keyDownEvent);
699 #endif
700     [pluginController release];
701     [toolTip release];
702 #if !PLATFORM(IOS)
703     [completionController release];
704 #endif
705     [dataSource release];
706 #if !PLATFORM(IOS)
707     if (promisedDragTIFFDataSource)
708         promisedDragTIFFDataSource->removeClient(promisedDataClient());
709 #endif
710
711     [super dealloc];
712 }
713
714 - (void)finalize
715 {
716 #if !PLATFORM(IOS)
717     if (promisedDragTIFFDataSource)
718         promisedDragTIFFDataSource->removeClient(promisedDataClient());
719 #endif
720
721     [super finalize];
722 }
723
724 - (void)clear
725 {
726 #if !PLATFORM(IOS)
727     [mouseDownEvent release];
728     [keyDownEvent release];
729 #else
730     if (mouseDownEvent)
731         CFRelease(mouseDownEvent);
732     if (keyDownEvent)
733         CFRelease(keyDownEvent);
734 #endif
735     [pluginController release];
736     [toolTip release];
737 #if !PLATFORM(IOS)
738     [completionController release];
739 #endif
740     [dataSource release];
741 #if !PLATFORM(IOS)
742     if (promisedDragTIFFDataSource)
743         promisedDragTIFFDataSource->removeClient(promisedDataClient());
744 #endif
745
746     mouseDownEvent = nil;
747     keyDownEvent = nil;
748     pluginController = nil;
749     toolTip = nil;
750 #if !PLATFORM(IOS)
751     completionController = nil;
752 #endif
753     dataSource = nil;
754 #if !PLATFORM(IOS)
755     promisedDragTIFFDataSource = 0;
756 #endif
757
758     layerHostingView = nil;
759 }
760
761 @end
762
763 @implementation WebHTMLView (WebHTMLViewFileInternal)
764
765 - (DOMRange *)_documentRange
766 {
767     return [[[self _frame] DOMDocument] _documentRange];
768 }
769
770 #if !PLATFORM(IOS)
771 - (BOOL)_imageExistsAtPaths:(NSArray *)paths
772 {
773     NSEnumerator *enumerator = [paths objectEnumerator];
774     NSString *path;
775     
776     while ((path = [enumerator nextObject]) != nil) {
777         NSString *MIMEType = [[NSURLFileTypeMappings sharedMappings] MIMETypeForExtension:[path pathExtension]];
778         if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType))
779             return YES;
780     }
781     
782     return NO;
783 }
784 #endif
785
786 - (WebDataSource *)_dataSource
787 {
788     return _private->dataSource;
789 }
790
791 - (WebView *)_webView
792 {
793     return [_private->dataSource _webView];
794 }
795
796 - (WebFrameView *)_frameView
797 {
798     return [[_private->dataSource webFrame] frameView];
799 }
800
801 #if !PLATFORM(IOS)
802 - (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths
803 {
804     DOMDocumentFragment *fragment;
805     NSEnumerator *enumerator = [paths objectEnumerator];
806     NSMutableArray *domNodes = [[NSMutableArray alloc] init];
807     NSString *path;
808     
809     while ((path = [enumerator nextObject]) != nil) {
810         // Non-image file types; _web_userVisibleString is appropriate here because this will
811         // be pasted as visible text.
812         NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString];
813         [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]];
814     }
815     
816     fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes]; 
817     
818     [domNodes release];
819     
820     return [fragment firstChild] != nil ? fragment : nil;
821 }
822
823 + (NSArray *)_excludedElementsForAttributedStringConversion
824 {
825     static NSArray *elements = nil;
826     if (elements == nil) {
827         elements = [[NSArray alloc] initWithObjects:
828             // Omit style since we want style to be inline so the fragment can be easily inserted.
829             @"style",
830             // Omit xml so the result is not XHTML.
831             @"xml", 
832             // Omit tags that will get stripped when converted to a fragment anyway.
833             @"doctype", @"html", @"head", @"body",
834             // Omit deprecated tags.
835             @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u",
836             // Omit object so no file attachments are part of the fragment.
837             @"object", nil];
838         CFRetain(elements);
839     }
840     return elements;
841 }
842
843 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
844 {
845     CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
846     NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
847     CFRelease(UUIDRef);
848     NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]];
849     CFRelease(UUIDString);
850
851     return URL;
852 }
853
854 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
855                                                inContext:(DOMRange *)context
856                                           allowPlainText:(BOOL)allowPlainText
857 {
858     NSArray *types = [pasteboard types];
859     DOMDocumentFragment *fragment = nil;
860
861     if ([types containsObject:WebArchivePboardType] &&
862         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
863                                                   forType:WebArchivePboardType
864                                                 inContext:context
865                                              subresources:0]))
866         return fragment;
867                                            
868     if ([types containsObject:NSFilenamesPboardType] &&
869         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
870                                                   forType:NSFilenamesPboardType
871                                                 inContext:context
872                                              subresources:0]))
873         return fragment;
874     
875     if ([types containsObject:NSHTMLPboardType] &&
876         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
877                                                   forType:NSHTMLPboardType
878                                                 inContext:context
879                                              subresources:0]))
880         return fragment;
881     
882     if ([types containsObject:NSRTFDPboardType] &&
883         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
884                                                   forType:NSRTFDPboardType
885                                                 inContext:context
886                                              subresources:0]))
887         return fragment;
888     
889     if ([types containsObject:NSRTFPboardType] &&
890         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
891                                                   forType:NSRTFPboardType
892                                                 inContext:context
893                                              subresources:0]))
894         return fragment;
895
896     if ([types containsObject:NSTIFFPboardType] &&
897         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
898                                                   forType:NSTIFFPboardType
899                                                 inContext:context
900                                              subresources:0]))
901         return fragment;
902
903     if ([types containsObject:NSPDFPboardType] &&
904         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
905                                                   forType:NSPDFPboardType
906                                                 inContext:context
907                                              subresources:0]))
908         return fragment;
909
910     if ([types containsObject:(NSString*)kUTTypePNG] &&
911         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
912                                                   forType:(NSString*)kUTTypePNG
913                                                 inContext:context
914                                              subresources:0]))
915         return fragment;
916         
917     if ([types containsObject:NSURLPboardType] &&
918         (fragment = [self _documentFragmentFromPasteboard:pasteboard 
919                                                   forType:NSURLPboardType
920                                                 inContext:context
921                                              subresources:0]))
922         return fragment;
923         
924     if (allowPlainText && [types containsObject:NSStringPboardType] &&
925         (fragment = [self _documentFragmentFromPasteboard:pasteboard
926                                                   forType:NSStringPboardType
927                                                 inContext:context
928                                              subresources:0])) {
929         return fragment;
930     }
931     
932     return nil;
933 }
934
935 - (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard
936 {
937     NSArray *types = [pasteboard types];
938     
939     if ([types containsObject:NSStringPboardType])
940         return [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping];
941     
942     NSAttributedString *attributedString = nil;
943     NSString *string;
944
945     if ([types containsObject:NSRTFDPboardType])
946         attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
947     if (attributedString == nil && [types containsObject:NSRTFPboardType])
948         attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
949     if (attributedString != nil) {
950         string = [[attributedString string] copy];
951         [attributedString release];
952         return [string autorelease];
953     }
954     
955     if ([types containsObject:NSFilenamesPboardType]) {
956         string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
957         if (string != nil)
958             return string;
959     }
960     
961     NSURL *URL;
962     
963     if ((URL = [NSURL URLFromPasteboard:pasteboard])) {
964         string = [URL _web_userVisibleString];
965         if ([string length] > 0)
966             return string;
967     }
968     
969     return nil;
970 }
971
972 - (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText
973 {
974     WebView *webView = [[self _webView] retain];
975     [webView _setInsertionPasteboard:pasteboard];
976
977     DOMRange *range = [self _selectedRange];
978     Frame* coreFrame = core([self _frame]);
979
980     DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText];
981     if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted])
982         coreFrame->editor().pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false);
983
984     [webView _setInsertionPasteboard:nil];
985     [webView release];
986 }
987
988 - (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard 
989
990     WebView *webView = [[self _webView] retain]; 
991     [webView _setInsertionPasteboard:pasteboard]; 
992
993     NSString *text = [self _plainTextFromPasteboard:pasteboard]; 
994     if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted]) 
995         [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]]; 
996
997     [webView _setInsertionPasteboard:nil]; 
998     [webView release];
999 }
1000
1001 - (void)_postFakeMouseMovedEventForFlagsChangedEvent:(NSEvent *)flagsChangedEvent
1002 {
1003     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved location:flagsChangedEvent.window.mouseLocationOutsideOfEventStream
1004         modifierFlags:flagsChangedEvent.modifierFlags timestamp:flagsChangedEvent.timestamp windowNumber:flagsChangedEvent.windowNumber
1005         context:flagsChangedEvent.context eventNumber:0 clickCount:0 pressure:0];
1006
1007     [self mouseMoved:fakeEvent];
1008 }
1009
1010 // This method is needed to support Mac OS X services.
1011 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard 
1012
1013     Frame* coreFrame = core([self _frame]); 
1014     if (!coreFrame) 
1015         return NO; 
1016     if (coreFrame->selection().selection().isContentRichlyEditable())
1017         [self _pasteWithPasteboard:pasteboard allowPlainText:YES]; 
1018     else 
1019         [self _pasteAsPlainTextWithPasteboard:pasteboard]; 
1020     return YES; 
1021 }
1022
1023 - (void)_removeSuperviewObservers
1024 {
1025     if (!_private || !_private->observingSuperviewNotifications)
1026         return;
1027     
1028     NSView *superview = [self superview];
1029     if (!superview || ![self window])
1030         return;
1031     
1032     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1033     [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview];
1034     [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview];
1035     
1036     _private->observingSuperviewNotifications = false;
1037 }
1038
1039 - (void)_removeWindowObservers
1040 {
1041     if (!_private->observingWindowNotifications)
1042         return;
1043     
1044     NSWindow *window = [self window];
1045     if (!window)
1046         return;
1047     
1048     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1049     [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
1050     [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
1051     [notificationCenter removeObserver:self name:WKWindowWillOrderOnScreenNotification() object:window];
1052     [notificationCenter removeObserver:self name:WKWindowWillOrderOffScreenNotification() object:window];
1053     [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window];
1054     
1055     _private->observingWindowNotifications = false;
1056 }
1057 #endif // !PLATFORM(IOS)
1058
1059 - (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
1060 {
1061     WebView *webView = [self _webView];
1062     DOMNode *child = [fragment firstChild];
1063     if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]])
1064         return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action];
1065     return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action];
1066 }
1067
1068 - (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
1069 {
1070     WebView *webView = [self _webView];
1071     return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action];
1072 }
1073
1074 - (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action
1075 {
1076     return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action];
1077 }
1078
1079 - (DOMRange *)_selectedRange
1080 {
1081     Frame* coreFrame = core([self _frame]);
1082     return coreFrame ? kit(coreFrame->selection().toNormalizedRange().get()) : nil;
1083 }
1084
1085 - (BOOL)_shouldDeleteRange:(DOMRange *)range
1086 {
1087     Frame* coreFrame = core([self _frame]);
1088     return coreFrame && coreFrame->editor().shouldDeleteRange(core(range));
1089 }
1090
1091 #if !PLATFORM(IOS)
1092 - (NSView *)_hitViewForEvent:(NSEvent *)event
1093 {
1094     // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView.  
1095     // Callers of this method, however, want to query the deepest view instead.
1096     forceNSViewHitTest = YES;
1097     NSView *hitView = [(NSView *)[[self window] contentView] hitTest:[event locationInWindow]];
1098     forceNSViewHitTest = NO;    
1099     return hitView;
1100 }
1101 #endif
1102
1103 #if !PLATFORM(IOS)
1104 - (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString
1105 {
1106     // Put HTML on the pasteboard.
1107     if ([types containsObject:WebArchivePboardType]) {
1108         if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) {
1109             if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0)
1110                 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType];
1111         }
1112     }
1113     
1114     // Put the attributed string on the pasteboard (RTF/RTFD format).
1115     if ([types containsObject:NSRTFDPboardType]) {
1116         if (attributedString == nil) {
1117             attributedString = [self selectedAttributedString];
1118         }        
1119         NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:@{ }];
1120         [pasteboard setData:RTFDData forType:NSRTFDPboardType];
1121     }        
1122     if ([types containsObject:NSRTFPboardType]) {
1123         if (!attributedString)
1124             attributedString = [self selectedAttributedString];
1125         if ([attributedString containsAttachments])
1126             attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
1127         NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:@{ }];
1128         [pasteboard setData:RTFData forType:NSRTFPboardType];
1129     }
1130     
1131     // Put plain string on the pasteboard.
1132     if ([types containsObject:NSStringPboardType]) {
1133         // Map &nbsp; to a plain old space because this is better for source code, other browsers do it,
1134         // and because HTML forces you to do this any time you want two spaces in a row.
1135         NSMutableString *s = [[self selectedString] mutableCopy];
1136         const unichar NonBreakingSpaceCharacter = 0xA0;
1137         NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1];
1138         [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
1139         [pasteboard setString:s forType:NSStringPboardType];
1140         [s release];
1141     }
1142     
1143     if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) {
1144         [pasteboard setData:nil forType:WebSmartPastePboardType];
1145     }
1146 }
1147 #endif // !PLATFORM(IOS)
1148
1149 #if !PLATFORM(IOS)
1150 - (void)_setMouseDownEvent:(NSEvent *)event
1151 #else
1152 - (void)_setMouseDownEvent:(WebEvent *)event
1153 #endif
1154 {
1155 #if !PLATFORM(IOS)
1156     ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
1157 #else
1158     ASSERT(!event || event.type == WebEventMouseDown);
1159 #endif
1160
1161     if (event == _private->mouseDownEvent)
1162         return;
1163
1164     [event retain];
1165     [_private->mouseDownEvent release];
1166     _private->mouseDownEvent = event;
1167 }
1168
1169 - (WebHTMLView *)_topHTMLView
1170 {
1171     // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing.
1172     WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView];
1173     ASSERT(!view || [view isKindOfClass:[WebHTMLView class]]);
1174     return view;
1175 }
1176
1177 - (BOOL)_isTopHTMLView
1178 {
1179     // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView).
1180     return self == [self _topHTMLView];
1181 }
1182
1183 #if !PLATFORM(IOS)
1184 - (void)_web_setPrintingModeRecursive
1185 {
1186     [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1187
1188 #ifndef NDEBUG
1189     _private->enumeratingSubviews = YES;
1190 #endif
1191
1192     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1193
1194     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1195
1196     unsigned count = [descendantWebHTMLViews count];
1197     for (unsigned i = 0; i < count; ++i)
1198         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1199
1200     [descendantWebHTMLViews release];
1201
1202 #ifndef NDEBUG
1203     _private->enumeratingSubviews = NO;
1204 #endif
1205 }
1206
1207 - (void)_web_clearPrintingModeRecursive
1208 {
1209     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1210
1211 #ifndef NDEBUG
1212     _private->enumeratingSubviews = YES;
1213 #endif
1214
1215     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1216
1217     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1218
1219     unsigned count = [descendantWebHTMLViews count];
1220     for (unsigned i = 0; i < count; ++i)
1221         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]];
1222
1223     [descendantWebHTMLViews release];
1224
1225 #ifndef NDEBUG
1226     _private->enumeratingSubviews = NO;
1227 #endif
1228 }
1229
1230 - (void)_web_setPrintingModeRecursiveAndAdjustViewSize
1231 {
1232     [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
1233
1234 #ifndef NDEBUG
1235     _private->enumeratingSubviews = YES;
1236 #endif
1237
1238     NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init];
1239
1240     [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews];
1241
1242     unsigned count = [descendantWebHTMLViews count];
1243     for (unsigned i = 0; i < count; ++i)
1244         [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
1245
1246     [descendantWebHTMLViews release];
1247
1248 #ifndef NDEBUG
1249     _private->enumeratingSubviews = NO;
1250 #endif
1251 }
1252 #endif // !PLATFORM(IOS)
1253
1254 @end
1255
1256 @implementation WebHTMLView (WebPrivate)
1257
1258 + (NSArray *)supportedMIMETypes
1259 {
1260     return [WebHTMLRepresentation supportedMIMETypes];
1261 }
1262
1263 + (NSArray *)supportedMediaMIMETypes
1264 {
1265     return [WebHTMLRepresentation supportedMediaMIMETypes];
1266 }
1267
1268 + (NSArray *)supportedImageMIMETypes
1269 {
1270     return [WebHTMLRepresentation supportedImageMIMETypes];
1271 }
1272
1273 + (NSArray *)supportedNonImageMIMETypes
1274 {
1275     return [WebHTMLRepresentation supportedNonImageMIMETypes];
1276 }
1277
1278 + (NSArray *)unsupportedTextMIMETypes
1279 {
1280     return [WebHTMLRepresentation unsupportedTextMIMETypes];
1281 }
1282
1283 #if PLATFORM(IOS)
1284 - (void)mouseMoved:(WebEvent *)event
1285 {
1286     Frame* frame = core([self _frame]);
1287     if (frame)
1288         frame->eventHandler().mouseMoved(event);
1289 }
1290 #endif
1291
1292 #if !PLATFORM(IOS)
1293 + (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent
1294 {
1295     // This is obsolete SPI needed only for older versions of Safari
1296 }
1297
1298 - (id)_bridge
1299 {
1300     // This method exists to maintain compatibility with Leopard's Dictionary.app, since it
1301     // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:.
1302     // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160>
1303     return [self _frame];
1304 }
1305
1306 - (void)_updateMouseoverWithFakeEvent
1307 {
1308 #pragma clang diagnostic push
1309 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1310     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1311         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1312         modifierFlags:[[NSApp currentEvent] modifierFlags]
1313         timestamp:[NSDate timeIntervalSinceReferenceDate]
1314         windowNumber:[[self window] windowNumber]
1315         context:[[NSApp currentEvent] context]
1316         eventNumber:0 clickCount:0 pressure:0];
1317 #pragma clang diagnostic pop
1318
1319     [self _updateMouseoverWithEvent:fakeEvent];
1320 }
1321 #endif // !PLATFORM(IOS)
1322
1323 - (void)_frameOrBoundsChanged
1324 {
1325     WebView *webView = [self _webView];
1326     WebDynamicScrollBarsView *scrollView = [[[webView mainFrame] frameView] _scrollView];
1327
1328     NSPoint origin = [[self superview] bounds].origin;
1329     if (!NSEqualPoints(_private->lastScrollPosition, origin) && ![scrollView inProgrammaticScroll]) {
1330         if (Frame* coreFrame = core([self _frame])) {
1331             if (FrameView* coreView = coreFrame->view()) {
1332                 _private->inScrollPositionChanged = YES;
1333                 coreView->scrollPositionChangedViaPlatformWidget(IntPoint(_private->lastScrollPosition), IntPoint(origin));
1334                 _private->inScrollPositionChanged = NO;
1335             }
1336         }
1337     
1338 #if !PLATFORM(IOS)
1339         [_private->completionController endRevertingChange:NO moveLeft:NO];
1340 #endif
1341         
1342         [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]];
1343     }
1344     _private->lastScrollPosition = origin;
1345 }
1346
1347 - (void)_setAsideSubviews
1348 {
1349 #if !PLATFORM(IOS)
1350     ASSERT(!_private->subviewsSetAside);
1351     ASSERT(_private->savedSubviews == nil);
1352     _private->savedSubviews = _subviews;
1353     // We need to keep the layer-hosting view in the subviews, otherwise the layers flash.
1354     if (_private->layerHostingView) {
1355         NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil];
1356         _subviews = newSubviews;
1357     } else
1358         _subviews = nil;
1359     _private->subviewsSetAside = YES;
1360 #endif
1361  }
1362  
1363  - (void)_restoreSubviews
1364  {
1365 #if !PLATFORM(IOS)
1366     ASSERT(_private->subviewsSetAside);
1367     if (_private->layerHostingView) {
1368         [_subviews release];
1369         _subviews = _private->savedSubviews;
1370     } else {
1371         ASSERT(_subviews == nil);
1372         _subviews = _private->savedSubviews;
1373     }
1374     _private->savedSubviews = nil;
1375     _private->subviewsSetAside = NO;
1376 #endif
1377 }
1378
1379 #ifndef NDEBUG
1380
1381 - (void)didAddSubview:(NSView *)subview
1382 {
1383     if (_private->enumeratingSubviews)
1384         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]));
1385 }
1386 #endif
1387
1388
1389 - (void)viewWillDraw
1390 {
1391     // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView
1392     // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed
1393     // once the FIXME in _isTopHTMLView is fixed.
1394     if (_private->dataSource && [self _isTopHTMLView]) {
1395         [self _web_updateLayoutAndStyleIfNeededRecursive];
1396         [[self _webView] _flushCompositingChanges];
1397     }
1398
1399     [super viewWillDraw];
1400 }
1401
1402
1403 #if !PLATFORM(IOS)
1404 // Don't let AppKit even draw subviews. We take care of that.
1405 - (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView
1406 {
1407     // This helps when we print as part of a larger print process.
1408     // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1409     BOOL wasInPrintingMode = _private->printing;
1410     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1411     if (isPrinting) {
1412         if (!wasInPrintingMode)
1413             [self _web_setPrintingModeRecursive];
1414         else
1415             [self _web_updateLayoutAndStyleIfNeededRecursive];
1416     } else if (wasInPrintingMode)
1417         [self _web_clearPrintingModeRecursive];
1418
1419     // There are known cases where -viewWillDraw is not called on all views being drawn.
1420     // See <rdar://problem/6964278> for example. Performing layout at this point prevents us from
1421     // trying to paint without layout (which WebCore now refuses to do, instead bailing out without
1422     // drawing at all), but we may still fail to update any regions dirtied by the layout which are
1423     // not already dirty. 
1424     if ([self _needsLayout]) {
1425         NSInteger rectCount;
1426         [self getRectsBeingDrawn:0 count:&rectCount];
1427         if (rectCount) {
1428             LOG_ERROR("View needs layout. Either -viewWillDraw wasn't called or layout was invalidated during the display operation. Performing layout now.");
1429             [self _web_updateLayoutAndStyleIfNeededRecursive];
1430         }
1431     }
1432
1433     [self _setAsideSubviews];
1434     [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView];
1435     [self _restoreSubviews];
1436
1437     if (wasInPrintingMode != isPrinting) {
1438         if (wasInPrintingMode)
1439             [self _web_setPrintingModeRecursive];
1440         else
1441             [self _web_clearPrintingModeRecursive];
1442     }
1443 }
1444
1445 // Don't let AppKit even draw subviews. We take care of that.
1446 - (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect
1447 {
1448     BOOL needToSetAsideSubviews = !_private->subviewsSetAside;
1449
1450     BOOL wasInPrintingMode = _private->printing;
1451     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
1452
1453     if (needToSetAsideSubviews) {
1454         // This helps when we print as part of a larger print process.
1455         // If the WebHTMLView itself is what we're printing, then we will never have to do this.
1456         if (isPrinting) {
1457             if (!wasInPrintingMode)
1458                 [self _web_setPrintingModeRecursive];
1459             else
1460                 [self _web_updateLayoutAndStyleIfNeededRecursive];
1461         } else if (wasInPrintingMode)
1462             [self _web_clearPrintingModeRecursive];
1463
1464
1465         [self _setAsideSubviews];
1466     }
1467
1468     [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect];
1469
1470     if (needToSetAsideSubviews) {
1471         if (wasInPrintingMode != isPrinting) {
1472             if (wasInPrintingMode)
1473                 [self _web_setPrintingModeRecursive];
1474             else
1475                 [self _web_clearPrintingModeRecursive];
1476         }
1477
1478         [self _restoreSubviews];
1479     }
1480 }
1481
1482 // Don't let AppKit even draw subviews. We take care of that.
1483 - (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView
1484 {
1485     [self _setAsideSubviews];
1486     [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView];
1487     [self _restoreSubviews];
1488 }
1489
1490 // Don't let AppKit even draw subviews. We take care of that.
1491 - (void)_recursive:(BOOL)recurseX displayRectIgnoringOpacity:(NSRect)displayRect inGraphicsContext:(NSGraphicsContext *)graphicsContext CGContext:(CGContextRef)ctx topView:(BOOL)isTopView shouldChangeFontReferenceColor:(BOOL)shouldChangeFontReferenceColor
1492 {
1493     BOOL didSetAsideSubviews = NO;
1494
1495     if (!_private->subviewsSetAside) {
1496         [self _setAsideSubviews];
1497         didSetAsideSubviews = YES;
1498     }
1499     
1500     [super _recursive:recurseX displayRectIgnoringOpacity:displayRect inGraphicsContext:graphicsContext CGContext:ctx topView:isTopView shouldChangeFontReferenceColor:shouldChangeFontReferenceColor];
1501
1502     if (didSetAsideSubviews)
1503         [self _restoreSubviews];
1504 }
1505 #endif // !PLATFORM(IOS)
1506
1507 - (BOOL)_insideAnotherHTMLView
1508 {
1509     return self != [self _topHTMLView];
1510 }
1511
1512 #if !PLATFORM(IOS)
1513 static BOOL isQuickLookEvent(NSEvent *event)
1514 {
1515     const int kCGSEventSystemSubtypeHotKeyCombinationReleased = 9;
1516     return [event type] == NSSystemDefined && [event subtype] == kCGSEventSystemSubtypeHotKeyCombinationReleased && [event data1] == 'lkup';
1517 }
1518 #endif
1519
1520 - (NSView *)hitTest:(NSPoint)point
1521 {
1522     // WebHTMLView objects handle all events for objects inside them.
1523     // To get those events, we prevent hit testing from AppKit.
1524
1525     // But there are three exceptions to this:
1526     //   1) For right mouse clicks and control clicks we don't yet have an implementation
1527     //      that works for nested views, so we let the hit testing go through the
1528     //      standard NSView code path (needs to be fixed, see bug 4361618).
1529     //   2) Java depends on doing a hit test inside it's mouse moved handling,
1530     //      so we let the hit testing go through the standard NSView code path
1531     //      when the current event is a mouse move (except when we are calling
1532     //      from _updateMouseoverWithEvent, so we have to use a global,
1533     //      forceWebHTMLViewHitTest, for that)
1534     //   3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods
1535     //      both need to figure out which view to check with inside the WebHTMLView.
1536     //      They use a global to change the behavior of hitTest: so they can get the
1537     //      right view. The global is forceNSViewHitTest and the method they use to
1538     //      do the hit testing is _hitViewForEvent:. (But this does not work correctly
1539     //      when there is HTML overlapping the view, see bug 4361626)
1540     //   4) NSAccessibilityHitTest relies on this for checking the cursor position.
1541     //      Our check for that is whether the event is NSFlagsChanged.  This works
1542     //      for VoiceOver's Control-Option-F5 command (move focus to item under cursor)
1543     //      and Dictionary's Command-Control-D (open dictionary popup for item under cursor).
1544     //      This is of course a hack.
1545
1546     if (_private->closed)
1547         return nil;
1548
1549 #if PLATFORM(IOS)
1550     // Preserve <rdar://problem/7992472> behavior for third party applications. See <rdar://problem/8463725>.
1551     if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_MULTIPLE_IFRAME_TOUCH_EVENT_DISPATCH)) {
1552         WebEvent *event = [WAKWindow currentEvent];
1553         if (event != NULL && event.type == WebEventMouseDown && [self mouse:point inRect:[self frame]])
1554             return self;
1555         NSView *view = [super hitTest:point];
1556         
1557         // Find the clicked document view
1558         while (view && ![view conformsToProtocol:@protocol(WebDocumentView)])
1559             view = [view superview];
1560             
1561         return view;
1562     }
1563 #else
1564     BOOL captureHitsOnSubviews;
1565     if (forceNSViewHitTest)
1566         captureHitsOnSubviews = NO;
1567     else if (forceWebHTMLViewHitTest)
1568         captureHitsOnSubviews = YES;
1569     else {
1570         // FIXME: Why doesn't this include mouse entered/exited events, or other mouse button events?
1571         NSEvent *event = [[self window] currentEvent];
1572         captureHitsOnSubviews = !([event type] == NSMouseMoved
1573             || [event type] == NSRightMouseDown
1574             || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0)
1575             || [event type] == NSFlagsChanged
1576             || isQuickLookEvent(event));
1577     }
1578
1579     if (!captureHitsOnSubviews) {
1580         NSView* hitView = [super hitTest:point];
1581         if (_private && hitView == _private->layerHostingView)
1582             hitView = self;
1583         return hitView;
1584     }
1585 #endif // !PLATFORM(IOS)
1586
1587     if ([[self superview] mouse:point inRect:[self frame]])
1588         return self;
1589     return nil;
1590 }
1591
1592 - (void)_clearLastHitViewIfSelf
1593 {
1594     if (lastHitView == self)
1595         lastHitView = nil;
1596 }
1597
1598 #if !PLATFORM(IOS)
1599 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
1600 {
1601     ASSERT(_private->trackingRectOwner == nil);
1602     _private->trackingRectOwner = owner;
1603     _private->trackingRectUserData = data;
1604     return TRACKING_RECT_TAG;
1605 }
1606
1607 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
1608 {
1609     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
1610     ASSERT(_private->trackingRectOwner == nil);
1611     _private->trackingRectOwner = owner;
1612     _private->trackingRectUserData = data;
1613     return TRACKING_RECT_TAG;
1614 }
1615
1616 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
1617 {
1618     ASSERT(count == 1);
1619     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
1620     ASSERT(_private->trackingRectOwner == nil);
1621     _private->trackingRectOwner = owner;
1622     _private->trackingRectUserData = userDataList[0];
1623     trackingNums[0] = TRACKING_RECT_TAG;
1624 }
1625
1626 - (void)removeTrackingRect:(NSTrackingRectTag)tag
1627 {
1628     if (tag == 0)
1629         return;
1630     
1631     if (_private && (tag == TRACKING_RECT_TAG)) {
1632         _private->trackingRectOwner = nil;
1633         return;
1634     }
1635     
1636     if (_private && (tag == _private->lastToolTipTag)) {
1637         [super removeTrackingRect:tag];
1638         _private->lastToolTipTag = 0;
1639         return;
1640     }
1641     
1642     // If any other tracking rect is being removed, we don't know how it was created
1643     // and it's possible there's a leak involved (see 3500217)
1644     ASSERT_NOT_REACHED();
1645 }
1646
1647 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
1648 {
1649     int i;
1650     for (i = 0; i < count; ++i) {
1651         int tag = tags[i];
1652         if (tag == 0)
1653             continue;
1654         ASSERT(tag == TRACKING_RECT_TAG);
1655         if (_private != nil) {
1656             _private->trackingRectOwner = nil;
1657         }
1658     }
1659 }
1660
1661 - (void)_sendToolTipMouseExited
1662 {
1663     // Nothing matters except window, trackingNumber, and userData.
1664     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
1665         location:NSMakePoint(0, 0)
1666         modifierFlags:0
1667         timestamp:0
1668         windowNumber:[[self window] windowNumber]
1669         context:NULL
1670         eventNumber:0
1671         trackingNumber:TRACKING_RECT_TAG
1672         userData:_private->trackingRectUserData];
1673     [_private->trackingRectOwner mouseExited:fakeEvent];
1674 }
1675
1676 - (void)_sendToolTipMouseEntered
1677 {
1678     // Nothing matters except window, trackingNumber, and userData.
1679     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
1680         location:NSMakePoint(0, 0)
1681         modifierFlags:0
1682         timestamp:0
1683         windowNumber:[[self window] windowNumber]
1684         context:NULL
1685         eventNumber:0
1686         trackingNumber:TRACKING_RECT_TAG
1687         userData:_private->trackingRectUserData];
1688     [_private->trackingRectOwner mouseEntered:fakeEvent];
1689 }
1690 #endif // !PLATFORM(IOS)
1691
1692 - (void)_setToolTip:(NSString *)string
1693 {
1694 #if !PLATFORM(IOS)
1695     NSString *toolTip = [string length] == 0 ? nil : string;
1696     NSString *oldToolTip = _private->toolTip;
1697     if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) {
1698         return;
1699     }
1700     if (oldToolTip) {
1701         [self _sendToolTipMouseExited];
1702         [oldToolTip release];
1703     }
1704     _private->toolTip = [toolTip copy];
1705     if (toolTip) {
1706         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
1707         [self removeAllToolTips];
1708         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
1709         _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
1710         [self _sendToolTipMouseEntered];
1711     }
1712 #endif
1713 }
1714
1715 #if !PLATFORM(IOS)
1716 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
1717 {
1718     return [[_private->toolTip copy] autorelease];
1719 }
1720
1721 static bool mouseEventIsPartOfClickOrDrag(NSEvent *event)
1722 {
1723     switch ([event type]) {
1724         case NSLeftMouseDown:
1725         case NSLeftMouseUp:
1726         case NSLeftMouseDragged:
1727         case NSRightMouseDown:
1728         case NSRightMouseUp:
1729         case NSRightMouseDragged:
1730         case NSOtherMouseDown:
1731         case NSOtherMouseUp:
1732         case NSOtherMouseDragged:
1733             return true;
1734         default:
1735             return false;
1736     }
1737 }
1738
1739 - (void)_updateMouseoverWithEvent:(NSEvent *)event
1740 {
1741     if (_private->closed)
1742         return;
1743
1744     NSView *contentView = [[event window] contentView];
1745     NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil];
1746     
1747     forceWebHTMLViewHitTest = YES;
1748     NSView *hitView = [contentView hitTest:locationForHitTest];
1749     forceWebHTMLViewHitTest = NO;
1750     
1751     WebHTMLView *view = nil;
1752     if ([hitView isKindOfClass:[WebHTMLView class]])
1753         view = (WebHTMLView *)hitView;    
1754
1755     if (view)
1756         [view retain];
1757
1758     if (lastHitView != view && lastHitView && [lastHitView _frame]) {
1759         // If we are moving out of a view (or frame), let's pretend the mouse moved
1760         // all the way out of that view. But we have to account for scrolling, because
1761         // WebCore doesn't understand our clipping.
1762         NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect];
1763         float yScroll = visibleRect.origin.y;
1764         float xScroll = visibleRect.origin.x;
1765
1766         NSEvent *event = [NSEvent mouseEventWithType:NSMouseMoved
1767             location:NSMakePoint(-1 - xScroll, -1 - yScroll)
1768             modifierFlags:[[NSApp currentEvent] modifierFlags]
1769             timestamp:[NSDate timeIntervalSinceReferenceDate]
1770             windowNumber:[[view window] windowNumber]
1771             context:[[NSApp currentEvent] context]
1772             eventNumber:0 clickCount:0 pressure:0];
1773         if (Frame* lastHitCoreFrame = core([lastHitView _frame]))
1774             lastHitCoreFrame->eventHandler().mouseMoved(event, [[self _webView] _pressureEvent]);
1775     }
1776
1777     lastHitView = view;
1778
1779     if (view) {
1780         if (Frame* coreFrame = core([view _frame])) {
1781             // We need to do a full, normal hit test during this mouse event if the page is active or if a mouse
1782             // button is currently pressed. It is possible that neither of those things will be true on Lion and
1783             // newer when legacy scrollbars are enabled, because then WebKit receives mouse events all the time. 
1784             // If it is one of those cases where the page is not active and the mouse is not pressed, then we can
1785             // fire a much more restricted and efficient scrollbars-only version of the event.
1786
1787             if ([[self window] isKeyWindow] 
1788                 || mouseEventIsPartOfClickOrDrag(event)
1789 #if ENABLE(DASHBOARD_SUPPORT)
1790                 || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]
1791 #endif
1792                 ) {
1793                 coreFrame->eventHandler().mouseMoved(event, [[self _webView] _pressureEvent]);
1794             } else
1795                 coreFrame->eventHandler().passMouseMovedEventToScrollbars(event, [[self _webView] _pressureEvent]);
1796         }
1797
1798         [view release];
1799     }
1800 }
1801
1802 + (NSArray *)_insertablePasteboardTypes
1803 {
1804     static NSArray *types = nil;
1805     if (!types) {
1806         types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType,
1807             NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil];
1808         CFRetain(types);
1809     }
1810     return types;
1811 }
1812
1813 + (NSArray *)_selectionPasteboardTypes
1814 {
1815     // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423).
1816     return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
1817 }
1818
1819 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
1820 {
1821     [self setPromisedDragTIFFDataSource:0];
1822 }
1823
1824 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
1825 {
1826     if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) {
1827         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
1828         [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]];
1829         [archive release];
1830     } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) {
1831         if (Image* image = [self promisedDragTIFFDataSource]->image())
1832             [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType];
1833         [self setPromisedDragTIFFDataSource:0];
1834     }
1835 }
1836
1837 - (void)_handleAutoscrollForMouseDragged:(NSEvent *)event 
1838
1839     [self autoscroll:event]; 
1840     [self _startAutoscrollTimer:event]; 
1841
1842 #endif // !PLATFORM(IOS)
1843
1844 - (WebPluginController *)_pluginController
1845 {
1846     return _private->pluginController;
1847 }
1848
1849 #if PLATFORM(IOS)
1850 // WAKView override.
1851 - (void)layoutIfNeeded
1852 {
1853     [self _layoutIfNeeded];
1854 }
1855
1856 // WAKView override.
1857 - (void)setScale:(float)scale
1858 {
1859     [super setScale:scale];
1860     Frame* coreFrame = core([self _frame]);
1861     if (!coreFrame)
1862         return;
1863
1864     if (Page* page = coreFrame->page())
1865         page->setPageScaleFactor(scale, IntPoint());
1866
1867     [[self _webView] _documentScaleChanged];
1868 }
1869
1870 #endif
1871
1872 #if !PLATFORM(IOS)
1873 - (void)_layoutForPrinting
1874 {
1875     // Set printing mode temporarily so we can adjust the size of the view. This will allow
1876     // AppKit's pagination code to use the correct height for the page content. Leaving printing
1877     // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just
1878     // turn it off again after adjusting the size.
1879     [self _web_setPrintingModeRecursiveAndAdjustViewSize];
1880     [self _web_clearPrintingModeRecursive];
1881 }
1882 #endif
1883
1884 #if !PLATFORM(IOS)
1885 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1886 {
1887     if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) {
1888         if (beforeString)
1889             *beforeString = nil;
1890         if (afterString)
1891             *afterString = nil;
1892         return;
1893     }
1894     
1895     [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString];
1896 }
1897
1898 - (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard
1899 {
1900     return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType];
1901 }
1902
1903 - (void)_startAutoscrollTimer:(NSEvent *)triggerEvent
1904 #else
1905 - (void)_startAutoscrollTimer: (WebEvent *)triggerEvent
1906 #endif
1907 {
1908     if (_private->autoscrollTimer == nil) {
1909         _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL
1910             target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain];
1911         _private->autoscrollTriggerEvent = [triggerEvent retain];
1912     }
1913 }
1914
1915 // FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection.
1916 // We can't remove this yet because it's still in use by Mail.
1917 - (NSRect)_selectionRect
1918 {
1919     return [self selectionRect];
1920 }
1921
1922 - (void)_stopAutoscrollTimer
1923 {
1924     NSTimer *timer = _private->autoscrollTimer;
1925     _private->autoscrollTimer = nil;
1926 #if !PLATFORM(IOS)
1927     [_private->autoscrollTriggerEvent release];
1928 #else
1929     if (_private->autoscrollTriggerEvent)
1930         CFRelease (_private->autoscrollTriggerEvent);
1931 #endif
1932     _private->autoscrollTriggerEvent = nil;
1933     [timer invalidate];
1934     [timer release];
1935 }
1936
1937 - (void)_autoscroll
1938 {
1939     // Guarantee that the autoscroll timer is invalidated, even if we don't receive
1940     // a mouse up event.
1941 #if !PLATFORM(IOS)
1942     BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft);   
1943 #else
1944     BOOL isStillDown = NO;
1945 #endif
1946     if (!isStillDown){
1947         [self _stopAutoscrollTimer];
1948         return;
1949     }
1950
1951 #if !PLATFORM(IOS)
1952 #pragma clang diagnostic push
1953 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1954     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
1955         location:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
1956         modifierFlags:[[NSApp currentEvent] modifierFlags]
1957         timestamp:[NSDate timeIntervalSinceReferenceDate]
1958         windowNumber:[[self window] windowNumber]
1959         context:[[NSApp currentEvent] context]
1960         eventNumber:0 clickCount:0 pressure:0];
1961 #pragma clang diagnostic pop
1962     [self mouseDragged:fakeEvent];
1963 #endif
1964 }
1965
1966 - (BOOL)_canEdit
1967 {
1968     Frame* coreFrame = core([self _frame]);
1969     return coreFrame && coreFrame->editor().canEdit();
1970 }
1971
1972 - (BOOL)_canEditRichly
1973 {
1974     Frame* coreFrame = core([self _frame]);
1975     return coreFrame && coreFrame->editor().canEditRichly();
1976 }
1977
1978 - (BOOL)_canAlterCurrentSelection
1979 {
1980     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
1981 }
1982
1983 - (BOOL)_hasSelection
1984 {
1985     Frame* coreFrame = core([self _frame]);
1986     return coreFrame && coreFrame->selection().selection().isRange();
1987 }
1988
1989 - (BOOL)_hasSelectionOrInsertionPoint
1990 {
1991     Frame* coreFrame = core([self _frame]);
1992     return coreFrame && coreFrame->selection().selection().isCaretOrRange();
1993 }
1994
1995 - (BOOL)_hasInsertionPoint
1996 {
1997     Frame* coreFrame = core([self _frame]);
1998     return coreFrame && coreFrame->selection().selection().isCaret();
1999 }
2000
2001 - (BOOL)_isEditable
2002 {
2003     Frame* coreFrame = core([self _frame]);
2004     return coreFrame && coreFrame->selection().selection().isContentEditable();
2005 }
2006
2007 - (BOOL)_transparentBackground
2008 {
2009     return _private->transparentBackground;
2010 }
2011
2012 - (void)_setTransparentBackground:(BOOL)f
2013 {
2014     _private->transparentBackground = f;
2015 }
2016
2017 #if !PLATFORM(IOS)
2018 - (NSImage *)_selectionDraggingImage
2019 {
2020     if (![self _hasSelection])
2021         return nil;
2022
2023     Frame* coreFrame = core([self _frame]);
2024     if (!coreFrame)
2025         return nil;
2026
2027     auto dragImage = createDragImageForSelection(*coreFrame);
2028     [dragImage _web_dissolveToFraction:WebDragImageAlpha];
2029
2030     return dragImage.autorelease();
2031 }
2032
2033 - (NSRect)_selectionDraggingRect
2034 {
2035     // Mail currently calls this method. We can eliminate it when Mail no longer calls it.
2036     return [self selectionRect];
2037 }
2038 #endif
2039
2040 - (DOMNode *)_insertOrderedList
2041 {
2042     Frame* coreFrame = core([self _frame]);
2043     return coreFrame ? kit(coreFrame->editor().insertOrderedList().get()) : nil;
2044 }
2045
2046 - (DOMNode *)_insertUnorderedList
2047 {
2048     Frame* coreFrame = core([self _frame]);
2049     return coreFrame ? kit(coreFrame->editor().insertUnorderedList().get()) : nil;
2050 }
2051
2052 - (BOOL)_canIncreaseSelectionListLevel
2053 {
2054     Frame* coreFrame = core([self _frame]);
2055     return coreFrame && coreFrame->editor().canIncreaseSelectionListLevel();
2056 }
2057
2058 - (BOOL)_canDecreaseSelectionListLevel
2059 {
2060     Frame* coreFrame = core([self _frame]);
2061     return coreFrame && coreFrame->editor().canDecreaseSelectionListLevel();
2062 }
2063
2064 - (DOMNode *)_increaseSelectionListLevel
2065 {
2066     Frame* coreFrame = core([self _frame]);
2067     return coreFrame ? kit(coreFrame->editor().increaseSelectionListLevel().get()) : nil;
2068 }
2069
2070 - (DOMNode *)_increaseSelectionListLevelOrdered
2071 {
2072     Frame* coreFrame = core([self _frame]);
2073     return coreFrame ? kit(coreFrame->editor().increaseSelectionListLevelOrdered().get()) : nil;
2074 }
2075
2076 - (DOMNode *)_increaseSelectionListLevelUnordered
2077 {
2078     Frame* coreFrame = core([self _frame]);
2079     return coreFrame ? kit(coreFrame->editor().increaseSelectionListLevelUnordered().get()) : nil;
2080 }
2081
2082 - (void)_decreaseSelectionListLevel
2083 {
2084     Frame* coreFrame = core([self _frame]);
2085     if (coreFrame)
2086         coreFrame->editor().decreaseSelectionListLevel();
2087 }
2088
2089 #if !PLATFORM(IOS)
2090 - (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard
2091 {
2092     ASSERT([self _hasSelection]);
2093     NSArray *types = [self pasteboardTypesForSelection];
2094
2095     // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
2096     NSAttributedString *attributedString = [self selectedAttributedString];
2097     NSMutableArray *mutableTypes = nil;
2098     if (![attributedString containsAttachments]) {
2099         mutableTypes = [types mutableCopy];
2100         [mutableTypes removeObject:NSRTFDPboardType];
2101         types = mutableTypes;
2102     }
2103
2104     [pasteboard declareTypes:types owner:[self _topHTMLView]];
2105     [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString];
2106     [mutableTypes release];
2107 }
2108 #endif
2109
2110 - (void)close
2111 {
2112     // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing
2113     // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case.
2114     if (!_private || _private->closed)
2115         return;
2116
2117     _private->closed = YES;
2118
2119     [self _clearLastHitViewIfSelf];
2120 #if !PLATFORM(IOS)
2121     [self _removeWindowObservers];
2122     [self _removeSuperviewObservers];
2123 #endif
2124     [_private->pluginController destroyAllPlugins];
2125     [_private->pluginController setDataSource:nil];
2126 #if !PLATFORM(IOS)
2127     // remove tooltips before clearing _private so removeTrackingRect: will work correctly
2128     [self removeAllToolTips];
2129
2130     if (_private->isInSecureInputState) {
2131         DisableSecureEventInput();
2132         _private->isInSecureInputState = NO;
2133     }
2134 #endif
2135
2136     [_private clear];
2137 }
2138
2139 - (BOOL)_hasHTMLDocument
2140 {
2141     Frame* coreFrame = core([self _frame]);
2142     if (!coreFrame)
2143         return NO;
2144     Document* document = coreFrame->document();
2145     return document && document->isHTMLDocument();
2146 }
2147
2148 #if !PLATFORM(IOS)
2149 - (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard
2150                                                  forType:(NSString *)pboardType
2151                                                inContext:(DOMRange *)context
2152                                             subresources:(NSArray **)subresources
2153 {
2154     if (pboardType == WebArchivePboardType) {
2155         WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]];
2156         if (subresources)
2157             *subresources = [archive subresources];
2158         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive];
2159         [archive release];
2160         return fragment;
2161     }
2162     if (pboardType == NSFilenamesPboardType)
2163         return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]];
2164         
2165     if (pboardType == NSHTMLPboardType) {
2166         NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType];
2167         // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
2168         if ([HTMLString hasPrefix:@"Version:"]) {
2169             NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
2170             if (range.location != NSNotFound)
2171                 HTMLString = [HTMLString substringFromIndex:range.location];
2172         }
2173         if ([HTMLString length] == 0)
2174             return nil;
2175         
2176         return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil];
2177     }
2178
2179     // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369.
2180     // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found".
2181     // FIXME: Remove this once bug 5052369 is fixed.
2182     if ([self _hasHTMLDocument] && (pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType)) {
2183         NSAttributedString *string = nil;
2184         if (pboardType == NSRTFDPboardType)
2185             string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL];
2186         if (string == nil)
2187             string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL];
2188         if (string == nil)
2189             return nil;
2190             
2191         NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
2192             [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute,
2193             self, @"WebResourceHandler", nil];
2194         NSArray *s;
2195         
2196         BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks];
2197         if (!wasDeferringCallbacks)
2198             [[self _webView] setDefersCallbacks:YES];
2199             
2200         DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length]) 
2201                                                           document:[[self _frame] DOMDocument] 
2202                                                 documentAttributes:documentAttributes
2203                                                       subresources:&s];
2204         if (subresources)
2205             *subresources = s;
2206         
2207         NSEnumerator *e = [s objectEnumerator];
2208         WebResource *r;
2209         while ((r = [e nextObject]))
2210             [[self _dataSource] addSubresource:r];
2211         
2212         if (!wasDeferringCallbacks)
2213             [[self _webView] setDefersCallbacks:NO];
2214         
2215         [documentAttributes release];
2216         [string release];
2217         return fragment;
2218     }
2219     if (pboardType == NSTIFFPboardType) {
2220         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType]
2221                                                               URL:uniqueURLWithRelativePart(@"image.tiff")
2222                                                          MIMEType:@"image/tiff" 
2223                                                  textEncodingName:nil
2224                                                         frameName:nil];
2225         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2226         [resource release];
2227         return fragment;
2228     }
2229     if (pboardType == NSPDFPboardType) {
2230         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPDFPboardType]
2231                                                               URL:uniqueURLWithRelativePart(@"application.pdf")
2232                                                          MIMEType:@"application/pdf" 
2233                                                  textEncodingName:nil
2234                                                         frameName:nil];
2235         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2236         [resource release];
2237         return fragment;
2238     }
2239
2240     if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) {
2241         WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG]
2242                                                               URL:uniqueURLWithRelativePart(@"image.png")
2243                                                          MIMEType:@"image/png" 
2244                                                  textEncodingName:nil
2245                                                         frameName:nil];
2246         DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource];
2247         [resource release];
2248         return fragment;
2249     }
2250     if (pboardType == NSURLPboardType) {
2251         NSURL *URL = [NSURL URLFromPasteboard:pasteboard];
2252         DOMDocument* document = [[self _frame] DOMDocument];
2253         ASSERT(document);
2254         if (!document)
2255             return nil;
2256         DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"];
2257         NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose.
2258         if ([URLString length] == 0)
2259             return nil;
2260         NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping];
2261         DOMText *text = [document createTextNode:URLTitleString];
2262         [anchor setHref:URLString];
2263         [anchor appendChild:text];
2264         DOMDocumentFragment *fragment = [document createDocumentFragment];
2265         [fragment appendChild:anchor];
2266         return fragment;
2267     }
2268     if (pboardType == NSStringPboardType) {
2269         if (!context)
2270             return nil;
2271         return kit(createFragmentFromText(*core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).ptr());
2272     }
2273     return nil;
2274 }
2275 #endif // !PLATFORM(IOS)
2276
2277 #if ENABLE(NETSCAPE_PLUGIN_API) 
2278 - (void)_pauseNullEventsForAllNetscapePlugins 
2279
2280     NSArray *subviews = [self subviews]; 
2281     unsigned int subviewCount = [subviews count]; 
2282     unsigned int subviewIndex; 
2283     
2284     for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { 
2285         NSView *subview = [subviews objectAtIndex:subviewIndex]; 
2286         if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) 
2287             [(WebBaseNetscapePluginView *)subview stopTimers];
2288     } 
2289
2290 #endif 
2291
2292 #if ENABLE(NETSCAPE_PLUGIN_API) 
2293 - (void)_resumeNullEventsForAllNetscapePlugins 
2294
2295     NSArray *subviews = [self subviews]; 
2296     unsigned int subviewCount = [subviews count]; 
2297     unsigned int subviewIndex; 
2298     
2299     for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { 
2300         NSView *subview = [subviews objectAtIndex:subviewIndex]; 
2301         if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) 
2302             [(WebBaseNetscapePluginView *)subview restartTimers]; 
2303     } 
2304
2305 #endif 
2306
2307 - (BOOL)_isUsingAcceleratedCompositing
2308 {
2309     return _private->layerHostingView != nil;
2310 }
2311
2312 - (NSView *)_compositingLayersHostingView
2313 {
2314     return _private->layerHostingView;
2315 }
2316
2317 - (BOOL)_isInPrintMode
2318 {
2319     return _private->printing;
2320 }
2321
2322 - (BOOL)_beginPrintModeWithMinimumPageWidth:(CGFloat)minimumPageWidth height:(CGFloat)minimumPageHeight maximumPageWidth:(CGFloat)maximumPageWidth
2323 {
2324     Frame* frame = core([self _frame]);
2325     if (!frame)
2326         return NO;
2327
2328     if (frame->document() && frame->document()->isFrameSet()) {
2329         minimumPageWidth = 0;
2330         minimumPageHeight = 0;
2331     }
2332
2333     float maximumShrinkRatio = 0;
2334     if (minimumPageWidth > 0.0)
2335         maximumShrinkRatio = maximumPageWidth / minimumPageWidth;
2336
2337     [self _setPrinting:YES minimumPageLogicalWidth:minimumPageWidth logicalHeight:minimumPageHeight originalPageWidth:minimumPageWidth originalPageHeight:minimumPageHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2338     return YES;
2339 }
2340
2341 - (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth height:(float)pageHeight shrinkToFit:(BOOL)shrinkToFit
2342 {
2343     Frame* frame = core([self _frame]);
2344     if (!frame)
2345         return NO;
2346
2347     Document* document = frame->document();
2348     bool isHorizontal = !document || !document->renderView() || document->renderView()->style().isHorizontalWritingMode();
2349
2350     float pageLogicalWidth = isHorizontal ? pageWidth : pageHeight;
2351     float pageLogicalHeight = isHorizontal ? pageHeight : pageWidth;
2352     FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2353     float maximumShrinkRatio = 1;
2354
2355     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2356     // according to the page width.
2357     if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2358         minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2359         maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2360     }
2361
2362     [self _setPrinting:YES minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() originalPageWidth:pageLogicalWidth originalPageHeight:pageLogicalHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2363
2364     return YES;
2365 }
2366
2367 - (void)_endPrintMode
2368 {
2369     [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2370 }
2371
2372 - (BOOL)_isInScreenPaginationMode
2373 {
2374     return _private->paginateScreenContent;
2375 }
2376
2377 - (BOOL)_beginScreenPaginationModeWithPageSize:(CGSize)pageSize shrinkToFit:(BOOL)shrinkToFit
2378 {
2379     Frame* frame = core([self _frame]);
2380     if (!frame)
2381         return NO;
2382
2383     Document* document = frame->document();
2384     bool isHorizontal = !document || !document->renderView() || document->renderView()->style().isHorizontalWritingMode();
2385
2386     float pageLogicalWidth = isHorizontal ? pageSize.width : pageSize.height;
2387     float pageLogicalHeight = isHorizontal ? pageSize.height : pageSize.width;
2388     FloatSize minLayoutSize(pageLogicalWidth, pageLogicalHeight);
2389     float maximumShrinkRatio = 1;
2390
2391     // If we are a frameset just print with the layout we have onscreen, otherwise relayout
2392     // according to the page width.
2393     if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) {
2394         minLayoutSize = frame->resizePageRectsKeepingRatio(FloatSize(pageLogicalWidth, pageLogicalHeight), FloatSize(pageLogicalWidth * _WebHTMLViewPrintingMinimumShrinkFactor, pageLogicalHeight * _WebHTMLViewPrintingMinimumShrinkFactor));
2395         maximumShrinkRatio = _WebHTMLViewPrintingMaximumShrinkFactor / _WebHTMLViewPrintingMinimumShrinkFactor;
2396     }
2397
2398     [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:minLayoutSize.width() logicalHeight:minLayoutSize.height() originalPageWidth:pageLogicalWidth originalPageHeight:pageLogicalHeight maximumShrinkRatio:maximumShrinkRatio adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]];
2399
2400     return YES;
2401 }
2402
2403 - (void)_endScreenPaginationMode
2404 {
2405     [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:0 logicalHeight:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustViewSize:YES paginateScreenContent:NO];
2406 }
2407
2408 - (CGFloat)_adjustedBottomOfPageWithTop:(CGFloat)top bottom:(CGFloat)bottom limit:(CGFloat)bottomLimit
2409 {
2410     Frame* frame = core([self _frame]);
2411     if (!frame)
2412         return bottom;
2413
2414     FrameView* view = frame->view();
2415     if (!view)
2416         return bottom;
2417
2418     float newBottom;
2419     view->adjustPageHeightDeprecated(&newBottom, top, bottom, bottomLimit);
2420
2421 #ifdef __LP64__
2422     // If the new bottom is equal to the old bottom (when both are treated as floats), we just return the original
2423     // bottom. This prevents rounding errors that can occur when converting newBottom to a double.
2424     if (WTF::areEssentiallyEqual(static_cast<float>(bottom), newBottom))
2425         return bottom;
2426     else
2427 #endif
2428         return newBottom;
2429 }
2430
2431 #if PLATFORM(IOS)
2432 - (id)accessibilityRootElement
2433 {
2434     return [[self _frame] accessibilityRoot];
2435 }
2436 #endif
2437
2438 @end
2439
2440 @implementation NSView (WebHTMLViewFileInternal)
2441
2442 - (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
2443 {
2444 #if PLATFORM(IOS)
2445     NSArray* _subviews = [self subviews];
2446 #endif
2447     unsigned count = [_subviews count];
2448     for (unsigned i = 0; i < count; ++i) {
2449         NSView *child = [_subviews objectAtIndex:i];
2450         if ([child isKindOfClass:[WebHTMLView class]])
2451             [array addObject:child];
2452         [child _web_addDescendantWebHTMLViewsToArray:array];
2453     }
2454 }
2455
2456 @end
2457
2458 @implementation NSMutableDictionary (WebHTMLViewFileInternal)
2459
2460 - (void)_web_setObjectIfNotNil:(id)object forKey:(id)key
2461 {
2462     if (object == nil) {
2463         [self removeObjectForKey:key];
2464     } else {
2465         [self setObject:object forKey:key];
2466     }
2467 }
2468
2469 @end
2470
2471 @implementation WebHTMLView
2472
2473 + (void)initialize
2474 {
2475 #if !PLATFORM(IOS)
2476     [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] 
2477                              returnTypes:[[self class] _insertablePasteboardTypes]];
2478     JSC::initializeThreading();
2479     WTF::initializeMainThreadToProcessMainThread();
2480     RunLoop::initializeMainRunLoop();
2481 #endif
2482     WebCoreObjCFinalizeOnMainThread(self);
2483 }
2484
2485 - (id)initWithFrame:(NSRect)frame
2486 {
2487     self = [super initWithFrame:frame];
2488     if (!self)
2489         return nil;
2490     
2491 #if !PLATFORM(IOS)
2492     [self setFocusRingType:NSFocusRingTypeNone];
2493 #endif
2494     
2495     // Make all drawing go through us instead of subviews.
2496     [self _setDrawsOwnDescendants:YES];
2497     
2498     _private = [[WebHTMLViewPrivate alloc] init];
2499
2500     _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self];
2501 #if PLATFORM(IOS)
2502     [[NSNotificationCenter defaultCenter] 
2503             addObserver:self selector:@selector(markedTextUpdate:) 
2504                    name:WebMarkedTextUpdatedNotification object:nil];
2505 #endif
2506     
2507     return self;
2508 }
2509
2510 - (void)dealloc
2511 {
2512     if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self))
2513         return;
2514
2515 #if PLATFORM(IOS)
2516     [[NSNotificationCenter defaultCenter] removeObserver:self name:WebMarkedTextUpdatedNotification object:nil];
2517 #endif
2518
2519     // We can't assert that close has already been called because
2520     // this view can be removed from it's superview, even though
2521     // it could be needed later, so close if needed.
2522     [self close];
2523     [_private release];
2524     _private = nil;
2525     [super dealloc];
2526 }
2527
2528 - (void)finalize
2529 {
2530     // We can't assert that close has already been called because
2531     // this view can be removed from it's superview, even though
2532     // it could be needed later, so close if needed.
2533     [self close];
2534     [super finalize];
2535 }
2536
2537 // Returns YES if the delegate returns YES (so we should do no more work).
2538 - (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector
2539 {
2540     BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector;
2541     _private->selectorForDoCommandBySelector = 0;
2542     if (callerAlreadyCalledDelegate)
2543         return NO;
2544     WebView *webView = [self _webView];
2545     return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector];
2546 }
2547
2548 typedef HashMap<SEL, String> SelectorNameMap;
2549
2550 // Map selectors into Editor command names.
2551 // This is not needed for any selectors that have the same name as the Editor command.
2552 static const SelectorNameMap* createSelectorExceptionMap()
2553 {
2554     SelectorNameMap* map = new HashMap<SEL, String>;
2555
2556     map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline");
2557     map->add(@selector(insertParagraphSeparator:), "InsertNewline");
2558     map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab");
2559     map->add(@selector(pageDown:), "MovePageDown");
2560     map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection");
2561     map->add(@selector(pageUp:), "MovePageUp");
2562     map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection");
2563
2564     return map;
2565 }
2566
2567 static String commandNameForSelector(SEL selector)
2568 {
2569     // Check the exception map first.
2570     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
2571     SelectorNameMap::const_iterator it = exceptionMap->find(selector);
2572     if (it != exceptionMap->end())
2573         return it->value;
2574
2575     // Remove the trailing colon.
2576     // No need to capitalize the command name since Editor command names are
2577     // not case sensitive.
2578     const char* selectorName = sel_getName(selector);
2579     size_t selectorNameLength = strlen(selectorName);
2580     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2581         return String();
2582     return String(selectorName, selectorNameLength - 1);
2583 }
2584
2585 - (Editor::Command)coreCommandBySelector:(SEL)selector
2586 {
2587     Frame* coreFrame = core([self _frame]);
2588     if (!coreFrame)
2589         return Editor::Command();
2590     return coreFrame->editor().command(commandNameForSelector(selector));
2591 }
2592
2593 - (Editor::Command)coreCommandByName:(const char*)name
2594 {
2595     Frame* coreFrame = core([self _frame]);
2596     if (!coreFrame)
2597         return Editor::Command();
2598     return coreFrame->editor().command(name);
2599 }
2600
2601 - (void)executeCoreCommandBySelector:(SEL)selector
2602 {
2603     if ([self callDelegateDoCommandBySelectorIfNeeded:selector])
2604         return;
2605     [self coreCommandBySelector:selector].execute();
2606 }
2607
2608 - (void)executeCoreCommandByName:(const char*)name
2609 {
2610     [self coreCommandByName:name].execute();
2611 }
2612
2613 // These commands are forwarded to the Editor object in WebCore.
2614 // Ideally we'd do this for all editing commands; more of the code
2615 // should be moved from here to there, and more commands should be
2616 // added to this list.
2617
2618 // FIXME: Maybe we should set things up so that all these share a single method implementation function.
2619 // The functions are identical.
2620
2621 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; }
2622
2623 WEBCORE_COMMAND(alignCenter)
2624 WEBCORE_COMMAND(alignJustified)
2625 WEBCORE_COMMAND(alignLeft)
2626 WEBCORE_COMMAND(alignRight)
2627 WEBCORE_COMMAND(copy)
2628 WEBCORE_COMMAND(cut)
2629 WEBCORE_COMMAND(paste)
2630 WEBCORE_COMMAND(delete)
2631 WEBCORE_COMMAND(deleteBackward)
2632 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
2633 WEBCORE_COMMAND(deleteForward)
2634 WEBCORE_COMMAND(deleteToBeginningOfLine)
2635 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
2636 WEBCORE_COMMAND(deleteToEndOfLine)
2637 WEBCORE_COMMAND(deleteToEndOfParagraph)
2638 WEBCORE_COMMAND(deleteToMark)
2639 WEBCORE_COMMAND(deleteWordBackward)
2640 WEBCORE_COMMAND(deleteWordForward)
2641 WEBCORE_COMMAND(ignoreSpelling)
2642 WEBCORE_COMMAND(indent)
2643 WEBCORE_COMMAND(insertBacktab)
2644 WEBCORE_COMMAND(insertLineBreak)
2645 WEBCORE_COMMAND(insertNewline)
2646 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
2647 WEBCORE_COMMAND(insertParagraphSeparator)
2648 WEBCORE_COMMAND(insertTab)
2649 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
2650 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
2651 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
2652 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
2653 WEBCORE_COMMAND(moveBackward)
2654 WEBCORE_COMMAND(moveBackwardAndModifySelection)
2655 WEBCORE_COMMAND(moveDown)
2656 WEBCORE_COMMAND(moveDownAndModifySelection)
2657 WEBCORE_COMMAND(moveForward)
2658 WEBCORE_COMMAND(moveForwardAndModifySelection)
2659 WEBCORE_COMMAND(moveLeft)
2660 WEBCORE_COMMAND(moveLeftAndModifySelection)
2661 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
2662 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
2663 WEBCORE_COMMAND(moveRight)
2664 WEBCORE_COMMAND(moveRightAndModifySelection)
2665 WEBCORE_COMMAND(moveToBeginningOfDocument)
2666 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
2667 WEBCORE_COMMAND(moveToBeginningOfLine)
2668 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
2669 WEBCORE_COMMAND(moveToBeginningOfParagraph)
2670 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
2671 WEBCORE_COMMAND(moveToBeginningOfSentence)
2672 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
2673 WEBCORE_COMMAND(moveToEndOfDocument)
2674 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
2675 WEBCORE_COMMAND(moveToEndOfLine)
2676 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
2677 WEBCORE_COMMAND(moveToEndOfParagraph)
2678 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
2679 WEBCORE_COMMAND(moveToEndOfSentence)
2680 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
2681 WEBCORE_COMMAND(moveToLeftEndOfLine)
2682 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
2683 WEBCORE_COMMAND(moveToRightEndOfLine)
2684 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
2685 WEBCORE_COMMAND(moveUp)
2686 WEBCORE_COMMAND(moveUpAndModifySelection)
2687 WEBCORE_COMMAND(moveWordBackward)
2688 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
2689 WEBCORE_COMMAND(moveWordForward)
2690 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
2691 WEBCORE_COMMAND(moveWordLeft)
2692 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
2693 WEBCORE_COMMAND(moveWordRight)
2694 WEBCORE_COMMAND(moveWordRightAndModifySelection)
2695 WEBCORE_COMMAND(outdent)
2696 WEBCORE_COMMAND(overWrite)
2697 WEBCORE_COMMAND(pageDown)
2698 WEBCORE_COMMAND(pageDownAndModifySelection)
2699 WEBCORE_COMMAND(pageUp)
2700 WEBCORE_COMMAND(pageUpAndModifySelection)
2701 WEBCORE_COMMAND(pasteAsPlainText)
2702 WEBCORE_COMMAND(selectAll)
2703 WEBCORE_COMMAND(selectLine)
2704 WEBCORE_COMMAND(selectParagraph)
2705 WEBCORE_COMMAND(selectSentence)
2706 WEBCORE_COMMAND(selectToMark)
2707 WEBCORE_COMMAND(selectWord)
2708 WEBCORE_COMMAND(setMark)
2709 WEBCORE_COMMAND(subscript)
2710 WEBCORE_COMMAND(superscript)
2711 WEBCORE_COMMAND(swapWithMark)
2712 WEBCORE_COMMAND(transpose)
2713 WEBCORE_COMMAND(underline)
2714 WEBCORE_COMMAND(unscript)
2715 WEBCORE_COMMAND(yank)
2716 WEBCORE_COMMAND(yankAndSelect)
2717
2718 #if PLATFORM(IOS)
2719 WEBCORE_COMMAND(clearText)
2720 WEBCORE_COMMAND(toggleBold)
2721 WEBCORE_COMMAND(toggleItalic)
2722 WEBCORE_COMMAND(toggleUnderline)
2723 #endif
2724
2725 #undef WEBCORE_COMMAND
2726
2727 #define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return;
2728
2729 #if !PLATFORM(IOS)
2730 - (IBAction)takeFindStringFromSelection:(id)sender
2731 {
2732     COMMAND_PROLOGUE
2733
2734     if (![self _hasSelection]) {
2735         NSBeep();
2736         return;
2737     }
2738
2739     [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self];
2740 }
2741
2742 // This method is needed to support Mac OS X services.
2743 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
2744 {
2745     [pasteboard declareTypes:types owner:[self _topHTMLView]];
2746     [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
2747     return YES;
2748 }
2749
2750 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
2751 {
2752     BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]);
2753     BOOL isReturnTypeOK = NO;
2754     if (!returnType)
2755         isReturnTypeOK = YES;
2756     else if ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) {
2757         // We can insert strings in any editable context.  We can insert other types, like images, only in rich edit contexts.
2758         isReturnTypeOK = [returnType isEqualToString:NSStringPboardType] || [self _canEditRichly];
2759     }
2760     if (isSendTypeOK && isReturnTypeOK)
2761         return self;
2762     return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
2763 }
2764 #endif
2765
2766 // jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
2767 // was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the
2768 // selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons:
2769 // (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
2770 // might be using the selector, and we don't want to break them.
2771 - (void)jumpToSelection:(id)sender
2772 {
2773     COMMAND_PROLOGUE
2774
2775     if (Frame* coreFrame = core([self _frame]))
2776         coreFrame->selection().revealSelection(ScrollAlignment::alignCenterAlways);
2777 }
2778
2779 #if !PLATFORM(IOS)
2780 - (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
2781 {
2782     SEL action = [item action];
2783     RefPtr<Frame> frame = core([self _frame]);
2784
2785     if (!frame)
2786         return NO;
2787     
2788     if (Document* doc = frame->document()) {
2789         if (doc->isPluginDocument())
2790             return NO;
2791         if (doc->isImageDocument()) {            
2792             if (action == @selector(copy:))
2793                 return frame->loader().isComplete();
2794             return NO;
2795         }
2796     }
2797
2798     if (action == @selector(changeSpelling:)
2799             || action == @selector(_changeSpellingFromMenu:)
2800             || action == @selector(checkSpelling:)
2801             || action == @selector(complete:)
2802             || action == @selector(pasteFont:))
2803         return [self _canEdit];
2804
2805     if (action == @selector(showGuessPanel:)) {
2806         // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior.
2807         NSMenuItem *menuItem = (NSMenuItem *)item;
2808         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2809             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
2810             [menuItem setTitle:panelShowing
2811                 ? UI_STRING_INTERNAL("Hide Spelling and Grammar", "menu item title")
2812                 : UI_STRING_INTERNAL("Show Spelling and Grammar", "menu item title")];
2813         }
2814         return [self _canEdit];
2815     }
2816     
2817     if (action == @selector(changeBaseWritingDirection:)
2818             || action == @selector(makeBaseWritingDirectionLeftToRight:)
2819             || action == @selector(makeBaseWritingDirectionRightToLeft:)) {
2820         NSWritingDirection writingDirection;
2821
2822         if (action == @selector(changeBaseWritingDirection:)) {
2823             writingDirection = static_cast<NSWritingDirection>([item tag]);
2824             if (writingDirection == NSWritingDirectionNatural)
2825                 return NO;
2826         } else if (action == @selector(makeBaseWritingDirectionLeftToRight:))
2827             writingDirection = NSWritingDirectionLeftToRight;
2828         else
2829             writingDirection = NSWritingDirectionRightToLeft;
2830
2831         NSMenuItem *menuItem = (NSMenuItem *)item;
2832         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2833             String direction = writingDirection == NSWritingDirectionLeftToRight ? "ltr" : "rtl";
2834             [menuItem setState:frame->editor().selectionHasStyle(CSSPropertyDirection, direction)];
2835         }
2836         return [self _canEdit];
2837     }
2838
2839     if (action == @selector(makeBaseWritingDirectionNatural:)) {
2840         NSMenuItem *menuItem = (NSMenuItem *)item;
2841         if ([menuItem isKindOfClass:[NSMenuItem class]])
2842             [menuItem setState:NSOffState];
2843         return NO;
2844     }
2845
2846     if (action == @selector(toggleBaseWritingDirection:)) {
2847         NSMenuItem *menuItem = (NSMenuItem *)item;
2848         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2849             // Take control of the title of the menu item instead of just checking/unchecking it because
2850             // a check would be ambiguous.
2851             [menuItem setTitle:frame->editor().selectionHasStyle(CSSPropertyDirection, "rtl")
2852                 ? UI_STRING_INTERNAL("Left to Right", "Left to Right context menu item")
2853                 : UI_STRING_INTERNAL("Right to Left", "Right to Left context menu item")];
2854         }
2855         return [self _canEdit];
2856     } 
2857     
2858     if (action == @selector(changeAttributes:)
2859             || action == @selector(changeColor:)        
2860             || action == @selector(changeFont:))
2861         return [self _canEditRichly];
2862     
2863     if (action == @selector(capitalizeWord:)
2864                || action == @selector(lowercaseWord:)
2865                || action == @selector(uppercaseWord:))
2866         return [self _hasSelection] && [self _isEditable];
2867
2868     if (action == @selector(centerSelectionInVisibleArea:)
2869                || action == @selector(jumpToSelection:)
2870                || action == @selector(copyFont:))
2871         return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
2872     
2873     if (action == @selector(changeDocumentBackgroundColor:))
2874         return [[self _webView] isEditable] && [self _canEditRichly];
2875     
2876     if (action == @selector(_ignoreSpellingFromMenu:)
2877             || action == @selector(_learnSpellingFromMenu:)
2878             || action == @selector(takeFindStringFromSelection:))
2879         return [self _hasSelection];
2880     
2881     if (action == @selector(paste:) || action == @selector(pasteAsPlainText:))
2882         return frame && (frame->editor().canDHTMLPaste() || frame->editor().canPaste());
2883     
2884     if (action == @selector(pasteAsRichText:))
2885         return frame && (frame->editor().canDHTMLPaste()
2886             || (frame->editor().canPaste() && frame->selection().selection().isContentRichlyEditable()));
2887     
2888     if (action == @selector(performFindPanelAction:))
2889         return NO;
2890     
2891     if (action == @selector(_lookUpInDictionaryFromMenu:))
2892         return [self _hasSelection];
2893
2894     if (action == @selector(stopSpeaking:))
2895         return [NSApp isSpeaking];
2896
2897     if (action == @selector(toggleGrammarChecking:)) {
2898         // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate 
2899         // the selector here because we implement it here, and we must implement it here because the AppKit 
2900         // code checks the first responder.
2901         NSMenuItem *menuItem = (NSMenuItem *)item;
2902         if ([menuItem isKindOfClass:[NSMenuItem class]])
2903             [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState];
2904         return YES;
2905     }
2906
2907     if (action == @selector(orderFrontSubstitutionsPanel:)) {
2908         NSMenuItem *menuItem = (NSMenuItem *)item;
2909         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
2910             BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
2911             [menuItem setTitle:panelShowing
2912                 ? UI_STRING_INTERNAL("Hide Substitutions", "menu item title")
2913                 : UI_STRING_INTERNAL("Show Substitutions", "menu item title")];
2914         }
2915         return [self _canEdit];
2916     }
2917     // FIXME 4799134: WebView is the bottleneck for this logic, but we must validate 
2918     // the selector here because we implement it here, and we must implement it here because the AppKit 
2919     // code checks the first responder.
2920     if (action == @selector(toggleSmartInsertDelete:)) {
2921         NSMenuItem *menuItem = (NSMenuItem *)item;
2922         if ([menuItem isKindOfClass:[NSMenuItem class]])
2923             [menuItem setState:[self smartInsertDeleteEnabled] ? NSOnState : NSOffState];
2924         return [self _canEdit];
2925     }
2926     if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
2927         NSMenuItem *menuItem = (NSMenuItem *)item;
2928         if ([menuItem isKindOfClass:[NSMenuItem class]])
2929             [menuItem setState:[self isAutomaticQuoteSubstitutionEnabled] ? NSOnState : NSOffState];
2930         return [self _canEdit];
2931     }
2932     if (action == @selector(toggleAutomaticLinkDetection:)) {
2933         NSMenuItem *menuItem = (NSMenuItem *)item;
2934         if ([menuItem isKindOfClass:[NSMenuItem class]])
2935             [menuItem setState:[self isAutomaticLinkDetectionEnabled] ? NSOnState : NSOffState];
2936         return [self _canEdit];
2937     }
2938     if (action == @selector(toggleAutomaticDashSubstitution:)) {
2939         NSMenuItem *menuItem = (NSMenuItem *)item;
2940         if ([menuItem isKindOfClass:[NSMenuItem class]])
2941             [menuItem setState:[self isAutomaticDashSubstitutionEnabled] ? NSOnState : NSOffState];
2942         return [self _canEdit];
2943     }
2944     if (action == @selector(toggleAutomaticTextReplacement:)) {
2945         NSMenuItem *menuItem = (NSMenuItem *)item;
2946         if ([menuItem isKindOfClass:[NSMenuItem class]])
2947             [menuItem setState:[self isAutomaticTextReplacementEnabled] ? NSOnState : NSOffState];
2948         return [self _canEdit];
2949     }
2950     if (action == @selector(toggleAutomaticSpellingCorrection:)) {
2951         NSMenuItem *menuItem = (NSMenuItem *)item;
2952         if ([menuItem isKindOfClass:[NSMenuItem class]])
2953             [menuItem setState:[self isAutomaticSpellingCorrectionEnabled] ? NSOnState : NSOffState];
2954         return [self _canEdit];
2955     }
2956
2957     Editor::Command command = [self coreCommandBySelector:action];
2958     if (command.isSupported()) {
2959         NSMenuItem *menuItem = (NSMenuItem *)item;
2960         if ([menuItem isKindOfClass:[NSMenuItem class]])
2961             [menuItem setState:kit(command.state())];
2962         return command.isEnabled();
2963     }
2964
2965     return YES;
2966 }
2967
2968 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
2969 {
2970     // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
2971     // assumes the WebVIew is non-nil.
2972     if (![self _webView])
2973         return NO;
2974     BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
2975     return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
2976 }
2977 #endif // !PLATFORM(IOS)
2978
2979 - (BOOL)acceptsFirstResponder
2980 {
2981     // Don't accept first responder when we first click on this view.
2982     // We have to pass the event down through WebCore first to be sure we don't hit a subview.
2983     // Do accept first responder at any other time, for example from keyboard events,
2984     // or from calls back from WebCore once we begin mouse-down event handling.
2985 #if !PLATFORM(IOS)            
2986     NSEvent *event = [NSApp currentEvent];
2987     if ([event type] == NSLeftMouseDown
2988             && !_private->handlingMouseDownEvent
2989             && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) {
2990         return NO;
2991     }
2992 #else
2993     WebEvent *event = [WAKWindow currentEvent];
2994     if (event && event.type == WebEventMouseDown
2995             && !_private->handlingMouseDownEvent
2996             && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil]))
2997         return NO;
2998 #endif
2999     return YES;
3000 }
3001
3002 - (BOOL)maintainsInactiveSelection
3003 {
3004 #if USE(UIKIT_EDITING)
3005     // We want to maintain an inactive selection, when in editable content.
3006     if ([[self _webView] maintainsInactiveSelection])
3007         return YES;
3008
3009     if ([[self window] _newFirstResponderAfterResigning] == self)
3010         return YES;
3011     
3012     Frame* coreFrame = core([self _frame]);
3013     return coreFrame && coreFrame->selection().selection().isContentEditable();
3014 #else
3015     // This method helps to determine whether the WebHTMLView should maintain
3016     // an inactive selection when it's not first responder.
3017     // Traditionally, these views have not maintained such selections,
3018     // clearing them when the view was not first responder. However,
3019     // to fix bugs like this one:
3020     // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even 
3021     //                            when they're not firstResponder"
3022     // it was decided to add a switch to act more like an NSTextView.
3023
3024     if ([[self _webView] maintainsInactiveSelection])
3025         return YES;
3026
3027     // Predict the case where we are losing first responder status only to
3028     // gain it back again. Want to keep the selection in that case.
3029     id nextResponder = [[self window] _newFirstResponderAfterResigning];
3030     if ([nextResponder isKindOfClass:[NSScrollView class]]) {
3031         id contentView = [nextResponder contentView];
3032         if (contentView)
3033             nextResponder = contentView;
3034     }
3035     if ([nextResponder isKindOfClass:[NSClipView class]]) {
3036         id documentView = [nextResponder documentView];
3037         if (documentView)
3038             nextResponder = documentView;
3039     }
3040     if (nextResponder == self)
3041         return YES;
3042
3043     Frame* coreFrame = core([self _frame]);
3044     bool selectionIsEditable = coreFrame && coreFrame->selection().selection().isContentEditable();
3045     bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]]
3046         && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]];
3047
3048     return selectionIsEditable && nextResponderIsInWebView;
3049 #endif
3050 }
3051
3052 #if !PLATFORM(IOS)
3053 - (void)addSuperviewObservers
3054 {
3055     if (_private->observingSuperviewNotifications)
3056         return;
3057
3058     NSView *superview = [self superview];
3059     if (!superview || ![self window])
3060         return;
3061     
3062     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
3063     [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview];
3064     [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview];
3065     
3066     // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged.
3067     // It will check the current scroll against the previous layout's scroll.  We need to
3068     // do this here to catch the case where the WebView is laid out at one size, removed from its
3069     // window, resized, and inserted into another window.  Our frame/bounds changed notifications
3070     // will not be sent in that situation, since we only watch for changes while in the view hierarchy.
3071     [self _frameOrBoundsChanged];
3072     
3073     _private->observingSuperviewNotifications = true;
3074 }
3075
3076 - (void)addWindowObservers
3077 {
3078     if (_private->observingWindowNotifications)
3079         return;
3080     
3081     NSWindow *window = [self window];
3082     if (!window)
3083         return;
3084     
3085     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
3086     [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
3087     [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
3088     [notificationCenter addObserver:self selector:@selector(windowWillOrderOnScreen:) name:WKWindowWillOrderOnScreenNotification() object:window];
3089     [notificationCenter addObserver:self selector:@selector(windowWillOrderOffScreen:) name:WKWindowWillOrderOffScreenNotification() object:window];
3090     [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window];
3091     
3092     _private->observingWindowNotifications = true;
3093 }
3094
3095 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
3096 {
3097     [self _removeSuperviewObservers];
3098 }
3099
3100 - (void)viewDidMoveToSuperview
3101 {
3102     if ([self superview] != nil)
3103         [self addSuperviewObservers];
3104
3105     if ([self superview] && [self _isUsingAcceleratedCompositing]) {
3106         WebView *webView = [self _webView];
3107         if ([webView _postsAcceleratedCompositingNotifications])
3108             [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:webView userInfo:nil];
3109     }
3110 }
3111 #endif // !PLATFORM(IOS)
3112
3113 - (void)viewWillMoveToWindow:(NSWindow *)window
3114 {
3115     // Don't do anything if we aren't initialized.  This happens
3116     // when decoding a WebView.  When WebViews are decoded their subviews
3117     // are created by initWithCoder: and so won't be normally
3118     // initialized.  The stub views are discarded by WebView.
3119     if (!_private)
3120         return;
3121
3122 #if !PLATFORM(IOS)
3123     // FIXME: Some of these calls may not work because this view may be already removed from it's superview.
3124     [self _removeWindowObservers];
3125     [self _removeSuperviewObservers];
3126 #endif
3127
3128     // FIXME: This accomplishes the same thing as the call to setCanStartMedia(false) in
3129     // WebView. It would be nice to have a single mechanism instead of two.
3130     [[self _pluginController] stopAllPlugins];
3131 }
3132
3133 - (void)viewDidMoveToWindow
3134 {
3135     // Don't do anything if we aren't initialized.  This happens
3136     // when decoding a WebView.  When WebViews are decoded their subviews
3137     // are created by initWithCoder: and so won't be normally
3138     // initialized.  The stub views are discarded by WebView.
3139     if (!_private || _private->closed)
3140         return;
3141         
3142     [self _stopAutoscrollTimer];
3143     if ([self window]) {
3144         _private->lastScrollPosition = [[self superview] bounds].origin;
3145 #if !PLATFORM(IOS)
3146         [self addWindowObservers];
3147         [self addSuperviewObservers];
3148 #endif
3149
3150         // FIXME: This accomplishes the same thing as the call to setCanStartMedia(true) in
3151         // WebView. It would be nice to have a single mechanism instead of two.
3152         [[self _pluginController] startAllPlugins];
3153
3154         _private->lastScrollPosition = NSZeroPoint;
3155
3156 #if !PLATFORM(IOS)
3157         if (!_private->flagsChangedEventMonitor) {
3158             _private->flagsChangedEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSFlagsChangedMask handler:^(NSEvent *flagsChangedEvent) {
3159                 [self _postFakeMouseMovedEventForFlagsChangedEvent:flagsChangedEvent];
3160                 return flagsChangedEvent;
3161             }];
3162         }
3163     } else {
3164         [NSEvent removeMonitor:_private->flagsChangedEventMonitor];
3165         _private->flagsChangedEventMonitor = nil;
3166 #endif
3167     }
3168 }
3169
3170 - (void)_web_makePluginSubviewsPerformSelector:(SEL)selector withObject:(id)object
3171 {
3172 #if ENABLE(NETSCAPE_PLUGIN_API)
3173     // Copy subviews because [self subviews] returns the view's mutable internal array,
3174     // and we must avoid mutating the array while enumerating it.
3175     NSArray *subviews = [[self subviews] copy];
3176     
3177     NSEnumerator *enumerator = [subviews objectEnumerator];
3178     WebNetscapePluginView *view;
3179     while ((view = [enumerator nextObject]) != nil)
3180         if ([view isKindOfClass:[WebBaseNetscapePluginView class]])
3181             [view performSelector:selector withObject:object];
3182     
3183     [subviews release];
3184 #endif
3185 }
3186
3187 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
3188 {
3189 #if !PLATFORM(IOS)
3190     [self _web_makePluginSubviewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow];
3191 #endif
3192 }
3193
3194 - (void)viewDidMoveToHostWindow
3195 {
3196 #if !PLATFORM(IOS)
3197     [self _web_makePluginSubviewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil];
3198 #endif
3199 }
3200
3201
3202 - (void)addSubview:(NSView *)view
3203 {
3204     [super addSubview:view];
3205
3206     if ([WebPluginController isPlugInView:view]) {
3207
3208 #if PLATFORM(IOS)
3209         WebView *webView = [self _webView];
3210         [[webView _UIKitDelegateForwarder] webView:webView willAddPlugInView:view];
3211 #endif
3212
3213         [[self _pluginController] addPlugin:view];
3214     }
3215 }
3216
3217 - (void)willRemoveSubview:(NSView *)subview
3218 {
3219 #ifndef NDEBUG
3220     // Have to null-check _private, since this can be called via -dealloc when
3221     // cleaning up the the layerHostingView.
3222     if (_private && _private->enumeratingSubviews)
3223         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]));
3224 #endif
3225
3226     if ([WebPluginController isPlugInView:subview])
3227         [[self _pluginController] destroyPlugin:subview];
3228
3229     [super willRemoveSubview:subview];
3230 }
3231
3232 - (void)reapplyStyles
3233 {
3234 #ifdef LOG_TIMES
3235     double start = CFAbsoluteTimeGetCurrent();
3236 #endif
3237
3238     if (Frame* coreFrame = core([self _frame]))
3239         coreFrame->document()->styleResolverChanged(RecalcStyleImmediately);
3240     
3241 #ifdef LOG_TIMES        
3242     double thisTime = CFAbsoluteTimeGetCurrent() - start;
3243     LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime);
3244 #endif
3245 }
3246
3247 // Do a layout, but set up a new fixed width for the purposes of doing printing layout.
3248 // minPageWidth==0 implies a non-printing layout
3249 - (void)layoutToMinimumPageWidth:(float)minPageLogicalWidth height:(float)minPageLogicalHeight originalPageWidth:(float)originalPageWidth originalPageHeight:(float)originalPageHeight maximumShrinkRatio:(float)maximumShrinkRatio adjustingViewSize:(BOOL)adjustViewSize
3250 {
3251     Frame* coreFrame = core([self _frame]);
3252     if (!coreFrame)
3253         return;
3254     if (coreFrame->document()) {
3255         if (coreFrame->document()->inPageCache())
3256             return;
3257         coreFrame->document()->updateStyleIfNeeded();
3258     }
3259
3260     if (![self _needsLayout])
3261         return;
3262
3263 #ifdef LOG_TIMES        
3264     double start = CFAbsoluteTimeGetCurrent();
3265 #endif
3266
3267     LOG(View, "%@ doing layout", self);
3268
3269     if (FrameView* coreView = coreFrame->view()) {
3270         if (minPageLogicalWidth > 0.0) {
3271             FloatSize pageSize(minPageLogicalWidth, minPageLogicalHeight);
3272             FloatSize originalPageSize(originalPageWidth, originalPageHeight);
3273             if (coreFrame->document() && coreFrame->document()->renderView() && !coreFrame->document()->renderView()->style().isHorizontalWritingMode()) {
3274                 pageSize = FloatSize(minPageLogicalHeight, minPageLogicalWidth);
3275                 originalPageSize = FloatSize(originalPageHeight, originalPageWidth);
3276             }
3277             coreView->forceLayoutForPagination(pageSize, originalPageSize, maximumShrinkRatio, adjustViewSize ? AdjustViewSize : DoNotAdjustViewSize);
3278         } else {
3279             coreView->forceLayout(!adjustViewSize);
3280             if (adjustViewSize)
3281                 coreView->adjustViewSize();
3282         }
3283     }
3284     
3285 #ifdef LOG_TIMES        
3286     double thisTime = CFAbsoluteTimeGetCurrent() - start;
3287     LOG(Timing, "%s layout seconds = %f", [self URL], thisTime);
3288 #endif
3289 }
3290
3291 - (void)layout
3292 {
3293     [self layoutToMinimumPageWidth:0 height:0 originalPageWidth:0 originalPageHeight:0 maximumShrinkRatio:0 adjustingViewSize:NO];
3294 }
3295
3296 #if !PLATFORM(IOS)
3297 // Deliver mouseup events to the DOM for button 2.
3298 - (void)rightMouseUp:(NSEvent *)event
3299 {
3300     // There's a chance that if we run a nested event loop the event will be released.
3301     // Retaining and then autoreleasing prevents that from causing a problem later here or
3302     // inside AppKit code.
3303     [[event retain] autorelease];
3304
3305     [super rightMouseUp:event];
3306
3307     if (Frame* coreframe = core([self _frame]))
3308         coreframe->eventHandler().mouseUp(event, [[self _webView] _pressureEvent]);
3309 }
3310
3311 static void setMenuItemTarget(NSMenuItem* menuItem)
3312 {
3313     // Don't set the menu item's action to the context menu action forwarder if we already
3314     // have an action.
3315     if ([menuItem action])
3316         return;
3317
3318     [menuItem setTarget:[WebMenuTarget sharedMenuTarget]];
3319     [menuItem setAction:@selector(forwardContextMenuAction:)];
3320 }
3321
3322 static void setMenuTargets(NSMenu* menu)
3323 {
3324     NSInteger itemCount = [menu numberOfItems];
3325     for (NSInteger i = 0; i < itemCount; ++i) {
3326         NSMenuItem *item = [menu itemAtIndex:i];
3327         setMenuItemTarget(item);
3328         if ([item hasSubmenu])
3329             setMenuTargets([item submenu]);
3330     }
3331 }
3332
3333 static BOOL isPreVersion3Client(void)
3334 {
3335     static BOOL preVersion3Client = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_3_0_CONTEXT_MENU_TAGS);
3336     return preVersion3Client;
3337 }
3338
3339 static BOOL isPreInspectElementTagClient(void)
3340 {
3341     static BOOL preInspectElementTagClient = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_INSPECT_ELEMENT_MENU_TAG);
3342     return preInspectElementTagClient;
3343 }
3344
3345 static RetainPtr<NSArray> fixMenusToSendToOldClients(NSMutableArray *defaultMenuItems)
3346 {
3347     auto savedItems = adoptNS([[NSMutableArray alloc] init]);
3348
3349     unsigned defaultItemsCount = [defaultMenuItems count];
3350
3351     if (isPreInspectElementTagClient() && defaultItemsCount >= 2) {
3352         NSMenuItem *secondToLastItem = [defaultMenuItems objectAtIndex:defaultItemsCount - 2];
3353         NSMenuItem *lastItem = [defaultMenuItems objectAtIndex:defaultItemsCount - 1];
3354
3355         if ([secondToLastItem isSeparatorItem] && [lastItem tag] == WebMenuItemTagInspectElement) {
3356             savedItems = adoptNS([[NSMutableArray alloc] initWithCapacity:2]);
3357             [savedItems addObject:secondToLastItem];
3358             [savedItems addObject:lastItem];
3359
3360             [defaultMenuItems removeObject:secondToLastItem];
3361             [defaultMenuItems removeObject:lastItem];
3362             defaultItemsCount -= 2;
3363         }
3364     }
3365
3366     BOOL preVersion3Client = isPreVersion3Client();
3367     if (!preVersion3Client)
3368         return savedItems;
3369
3370     for (NSMenuItem *item in defaultMenuItems) {
3371         int tag = item.tag;
3372         int oldStyleTag = tag;
3373
3374         if (tag >= WEBMENUITEMTAG_WEBKIT_3_0_SPI_START) {
3375             // Change all editing-related SPI tags listed in WebUIDelegatePrivate.h to WebMenuItemTagOther
3376             // to match our old WebKit context menu behavior.
3377             oldStyleTag = WebMenuItemTagOther;
3378         } else {
3379             // All items are expected to have useful tags coming into this method.
3380             ASSERT(tag != WebMenuItemTagOther);
3381             
3382             // Use the pre-3.0 tags for the few items that changed tags as they moved from SPI to API. We
3383             // do this only for old clients; new Mail already expects the new symbols in this case.
3384             if (preVersion3Client) {
3385                 switch (tag) {
3386                 case WebMenuItemTagSearchInSpotlight:
3387                     oldStyleTag = OldWebMenuItemTagSearchInSpotlight;
3388                     break;
3389                 case WebMenuItemTagSearchWeb:
3390                     oldStyleTag = OldWebMenuItemTagSearchWeb;
3391                     break;
3392                 case WebMenuItemTagLookUpInDictionary:
3393                     oldStyleTag = OldWebMenuItemTagLookUpInDictionary;
3394                     break;
3395                 default:
3396                     break;
3397                 }
3398             }
3399         }
3400
3401         item.tag = oldStyleTag;
3402     }
3403
3404     return savedItems;
3405 }
3406
3407 static RetainPtr<NSArray> fixMenusReceivedFromOldClients(NSArray *delegateSuppliedItems, NSArray *savedItems)
3408 {
3409     auto newMenuItems = adoptNS([delegateSuppliedItems mutableCopy]);
3410
3411     if (savedItems)
3412         [newMenuItems addObjectsFromArray:savedItems];
3413
3414     BOOL preVersion3Client = isPreVersion3Client();
3415     if (!preVersion3Client)
3416         return newMenuItems;
3417     
3418     // Restore the modern tags to the menu items whose tags we altered in fixMenusToSendToOldClients. 
3419     unsigned newItemsCount = [newMenuItems count];
3420     for (unsigned i = 0; i < newItemsCount; ++i) {
3421         NSMenuItem *item = [newMenuItems objectAtIndex:i];
3422         
3423         int tag = [item tag];
3424         int modernTag = tag;
3425         
3426         if (tag == WebMenuItemTagOther) {
3427             // Restore the specific tag for items on which we temporarily set WebMenuItemTagOther to match old behavior.
3428             NSString *title = [item title];
3429             if ([title isEqualToString:contextMenuItemTagOpenLink()])
3430                 modernTag = WebMenuItemTagOpenLink;
3431             else if ([title isEqualToString:contextMenuItemTagIgnoreGrammar()])
3432                 modernTag = WebMenuItemTagIgnoreGrammar;
3433             else if ([title isEqualToString:contextMenuItemTagSpellingMenu()])
3434                 modernTag = WebMenuItemTagSpellingMenu;
3435             else if ([title isEqualToString:contextMenuItemTagShowSpellingPanel(true)] || [title isEqualToString:contextMenuItemTagShowSpellingPanel(false)])
3436                 modernTag = WebMenuItemTagShowSpellingPanel;
3437             else if ([title isEqualToString:contextMenuItemTagCheckSpelling()])
3438                 modernTag = WebMenuItemTagCheckSpelling;
3439             else if ([title isEqualToString:contextMenuItemTagCheckSpellingWhileTyping()])
3440                 modernTag = WebMenuItemTagCheckSpellingWhileTyping;
3441             else if ([title isEqualToString:contextMenuItemTagCheckGrammarWithSpelling()])
3442                 modernTag = WebMenuItemTagCheckGrammarWithSpelling;
3443             else if ([title isEqualToString:contextMenuItemTagFontMenu()])
3444                 modernTag = WebMenuItemTagFontMenu;
3445             else if ([title isEqualToString:contextMenuItemTagShowFonts()])
3446                 modernTag = WebMenuItemTagShowFonts;
3447             else if ([title isEqualToString:contextMenuItemTagBold()])
3448                 modernTag = WebMenuItemTagBold;
3449             else if ([title isEqualToString:contextMenuItemTagItalic()])
3450                 modernTag = WebMenuItemTagItalic;
3451             else if ([title isEqualToString:contextMenuItemTagUnderline()])
3452                 modernTag = WebMenuItemTagUnderline;
3453             else if ([title isEqualToString:contextMenuItemTagOutline()])
3454                 modernTag = WebMenuItemTagOutline;
3455             else if ([title isEqualToString:contextMenuItemTagStyles()])
3456                 modernTag = WebMenuItemTagStyles;
3457             else if ([title isEqualToString:contextMenuItemTagShowColors()])
3458                 modernTag = WebMenuItemTagShowColors;
3459             else if ([title isEqualToString:contextMenuItemTagSpeechMenu()])
3460                 modernTag = WebMenuItemTagSpeechMenu;
3461             else if ([title isEqualToString:contextMenuItemTagStartSpeaking()])
3462                 modernTag = WebMenuItemTagStartSpeaking;
3463             else if ([title isEqualToString:contextMenuItemTagStopSpeaking()])
3464                 modernTag = WebMenuItemTagStopSpeaking;
3465             else if ([title isEqualToString:contextMenuItemTagWritingDirectionMenu()])
3466                 modernTag = WebMenuItemTagWritingDirectionMenu;
3467             else if ([title isEqualToString:contextMenuItemTagDefaultDirection()])
3468                 modernTag = WebMenuItemTagDefaultDirection;
3469             else if ([title isEqualToString:contextMenuItemTagLe