Cannot invoke action menus anymore
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / mac / WKView.mm
1 /*
2  * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKView.h"
28
29 #if PLATFORM(MAC)
30
31 #if USE(DICTATION_ALTERNATIVES)
32 #import <AppKit/NSTextAlternatives.h>
33 #import <AppKit/NSAttributedString.h>
34 #endif
35
36 #import "APILegacyContextHistoryClient.h"
37 #import "APIPageConfiguration.h"
38 #import "ActionMenuHitTestResult.h"
39 #import "AttributedString.h"
40 #import "ColorSpaceData.h"
41 #import "DataReference.h"
42 #import "EditingRange.h"
43 #import "EditorState.h"
44 #import "LayerTreeContext.h"
45 #import "Logging.h"
46 #import "NativeWebKeyboardEvent.h"
47 #import "NativeWebMouseEvent.h"
48 #import "NativeWebWheelEvent.h"
49 #import "PageClientImpl.h"
50 #import "PasteboardTypes.h"
51 #import "RemoteLayerTreeDrawingAreaProxy.h"
52 #import "StringUtilities.h"
53 #import "TextChecker.h"
54 #import "TextCheckerState.h"
55 #import "TiledCoreAnimationDrawingAreaProxy.h"
56 #import "ViewGestureController.h"
57 #import "ViewSnapshotStore.h"
58 #import "WKAPICast.h"
59 #import "WKActionMenuController.h"
60 #import "WKActionMenuItemTypes.h"
61 #import "WKFullScreenWindowController.h"
62 #import "WKImmediateActionController.h"
63 #import "WKPrintingView.h"
64 #import "WKProcessPoolInternal.h"
65 #import "WKStringCF.h"
66 #import "WKTextInputWindowController.h"
67 #import "WKViewInternal.h"
68 #import "WKViewPrivate.h"
69 #import "WebBackForwardList.h"
70 #import "WebEventFactory.h"
71 #import "WebKit2Initialize.h"
72 #import "WebPage.h"
73 #import "WebPageGroup.h"
74 #import "WebPageProxy.h"
75 #import "WebPreferences.h"
76 #import "WebProcessPool.h"
77 #import "WebProcessProxy.h"
78 #import "WebSystemInterface.h"
79 #import "_WKThumbnailViewInternal.h"
80 #import <QuartzCore/QuartzCore.h>
81 #import <WebCore/AXObjectCache.h>
82 #import <WebCore/ColorMac.h>
83 #import <WebCore/DataDetectorsSPI.h>
84 #import <WebCore/DragController.h>
85 #import <WebCore/DragData.h>
86 #import <WebCore/FloatRect.h>
87 #import <WebCore/Image.h>
88 #import <WebCore/IntRect.h>
89 #import <WebCore/FileSystem.h>
90 #import <WebCore/KeyboardEvent.h>
91 #import <WebCore/LocalizedStrings.h>
92 #import <WebCore/LookupSPI.h>
93 #import <WebCore/NSImmediateActionGestureRecognizerSPI.h>
94 #import <WebCore/NSMenuSPI.h>
95 #import <WebCore/NSViewSPI.h>
96 #import <WebCore/PlatformEventFactoryMac.h>
97 #import <WebCore/PlatformScreen.h>
98 #import <WebCore/Region.h>
99 #import <WebCore/RuntimeApplicationChecks.h>
100 #import <WebCore/SharedBuffer.h>
101 #import <WebCore/SoftLinking.h>
102 #import <WebCore/TextAlternativeWithRange.h>
103 #import <WebCore/TextIndicator.h>
104 #import <WebCore/TextIndicatorWindow.h>
105 #import <WebCore/TextUndoInsertionMarkupMac.h>
106 #import <WebCore/WebActionDisablingCALayerDelegate.h>
107 #import <WebCore/WebCoreCALayerExtras.h>
108 #import <WebCore/WebCoreFullScreenPlaceholderView.h>
109 #import <WebCore/WebCoreFullScreenWindow.h>
110 #import <WebCore/WebCoreNSStringExtras.h>
111 #import <WebKitSystemInterface.h>
112 #import <sys/stat.h>
113 #import <wtf/RefPtr.h>
114 #import <wtf/RetainPtr.h>
115 #import <wtf/RunLoop.h>
116
117 /* API internals. */
118 #import "WKBrowsingContextControllerInternal.h"
119 #import "WKBrowsingContextGroupPrivate.h"
120 #import "WKProcessGroupPrivate.h"
121
122 @interface NSApplication (WKNSApplicationDetails)
123 - (void)speakString:(NSString *)string;
124 - (void)_setCurrentEvent:(NSEvent *)event;
125 @end
126
127 @interface NSWindow (WKNSWindowDetails)
128 - (NSRect)_intersectBottomCornersWithRect:(NSRect)viewRect;
129 - (void)_maskRoundedBottomCorners:(NSRect)clipRect;
130 @end
131
132 #if USE(ASYNC_NSTEXTINPUTCLIENT)
133 @interface NSTextInputContext (WKNSTextInputContextDetails)
134 - (void)handleEvent:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler;
135 - (void)handleEventByInputMethod:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler;
136 - (BOOL)handleEventByKeyboardLayout:(NSEvent *)theEvent;
137 @end
138 #endif
139
140 #if defined(__has_include) && __has_include(<CoreGraphics/CoreGraphicsPrivate.h>)
141 #import <CoreGraphics/CoreGraphicsPrivate.h>
142 #endif
143
144 extern "C" {
145 typedef uint32_t CGSConnectionID;
146 typedef uint32_t CGSWindowID;
147 CGSConnectionID CGSMainConnectionID(void);
148 CGError CGSGetScreenRectForWindow(CGSConnectionID cid, CGSWindowID wid, CGRect *rect);
149 };
150
151 SOFT_LINK_CONSTANT_MAY_FAIL(Lookup, LUNotificationPopoverWillClose, NSString *)
152
153 using namespace WebKit;
154 using namespace WebCore;
155
156 namespace WebKit {
157
158 typedef id <NSValidatedUserInterfaceItem> ValidationItem;
159 typedef Vector<RetainPtr<ValidationItem>> ValidationVector;
160 typedef HashMap<String, ValidationVector> ValidationMap;
161
162 }
163
164 #if !USE(ASYNC_NSTEXTINPUTCLIENT)
165 struct WKViewInterpretKeyEventsParameters {
166     bool eventInterpretationHadSideEffects;
167     bool consumedByIM;
168     bool executingSavedKeypressCommands;
169     Vector<KeypressCommand>* commands;
170 };
171 #endif
172
173 @interface WKViewData : NSObject {
174 @public
175     std::unique_ptr<PageClientImpl> _pageClient;
176     RefPtr<WebPageProxy> _page;
177
178 #if WK_API_ENABLED
179     RetainPtr<WKBrowsingContextController> _browsingContextController;
180 #endif
181
182     RetainPtr<NSTrackingArea> _primaryTrackingArea;
183
184     // For ToolTips.
185     NSToolTipTag _lastToolTipTag;
186     id _trackingRectOwner;
187     void* _trackingRectUserData;
188
189     RetainPtr<NSView> _layerHostingView;
190
191     RetainPtr<id> _remoteAccessibilityChild;
192     
193     // For asynchronous validation.
194     ValidationMap _validationMap;
195
196     std::unique_ptr<TextIndicatorWindow> _textIndicatorWindow;
197
198     // We keep here the event when resending it to
199     // the application to distinguish the case of a new event from one 
200     // that has been already sent to WebCore.
201     RetainPtr<NSEvent> _keyDownEventBeingResent;
202 #if USE(ASYNC_NSTEXTINPUTCLIENT)
203     Vector<KeypressCommand>* _collectedKeypressCommands;
204 #else
205     WKViewInterpretKeyEventsParameters* _interpretKeyEventsParameters;
206 #endif
207
208     NSSize _resizeScrollOffset;
209
210     // The identifier of the plug-in we want to send complex text input to, or 0 if there is none.
211     uint64_t _pluginComplexTextInputIdentifier;
212
213     // The state of complex text input for the plug-in.
214     PluginComplexTextInputState _pluginComplexTextInputState;
215
216     bool _inBecomeFirstResponder;
217     bool _inResignFirstResponder;
218     NSEvent *_mouseDownEvent;
219     BOOL _ignoringMouseDraggedEvents;
220
221     id _flagsChangedEventMonitor;
222
223 #if ENABLE(FULLSCREEN_API)
224     RetainPtr<WKFullScreenWindowController> _fullScreenWindowController;
225 #endif
226
227     BOOL _hasSpellCheckerDocumentTag;
228     NSInteger _spellCheckerDocumentTag;
229
230     BOOL _inSecureInputState;
231
232     NSRect _windowBottomCornerIntersectionRect;
233     
234     unsigned _frameSizeUpdatesDisabledCount;
235     BOOL _shouldDeferViewInWindowChanges;
236     NSWindow *_targetWindowForMovePreparation;
237
238     BOOL _viewInWindowChangeWasDeferred;
239
240     BOOL _needsViewFrameInWindowCoordinates;
241     BOOL _didScheduleWindowAndViewFrameUpdate;
242
243     RetainPtr<NSColorSpace> _colorSpace;
244
245     RefPtr<WebCore::Image> _promisedImage;
246     String _promisedFilename;
247     String _promisedURL;
248     
249     NSSize _intrinsicContentSize;
250     BOOL _clipsToVisibleRect;
251     NSRect _contentPreparationRect;
252     BOOL _useContentPreparationRectForVisibleRect;
253     BOOL _windowOcclusionDetectionEnabled;
254
255     std::unique_ptr<ViewGestureController> _gestureController;
256     BOOL _allowsMagnification;
257     BOOL _ignoresNonWheelEvents;
258     BOOL _ignoresAllEvents;
259     BOOL _allowsBackForwardNavigationGestures;
260
261     RetainPtr<CALayer> _rootLayer;
262
263     BOOL _didScheduleSetTopContentInset;
264     CGFloat _topContentInset;
265     CGFloat _totalHeightOfBanners;
266
267     CGFloat _overrideDeviceScaleFactor;
268
269 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
270     BOOL _automaticallyAdjustsContentInsets;
271     RetainPtr<WKActionMenuController> _actionMenuController;
272     RetainPtr<WKImmediateActionController> _immediateActionController;
273     RetainPtr<NSImmediateActionGestureRecognizer> _immediateActionGestureRecognizer;
274 #endif
275
276 #if WK_API_ENABLED
277     _WKThumbnailView *_thumbnailView;
278 #endif
279 }
280
281 @end
282
283 @implementation WKViewData
284 @end
285
286 @interface WKResponderChainSink : NSResponder {
287     NSResponder *_lastResponderInChain;
288     bool _didReceiveUnhandledCommand;
289 }
290 - (id)initWithResponderChain:(NSResponder *)chain;
291 - (void)detach;
292 - (bool)didReceiveUnhandledCommand;
293 @end
294
295 @interface WKFlippedView : NSView
296 @end
297
298 @implementation WKFlippedView
299
300 - (BOOL)isFlipped
301 {
302     return YES;
303 }
304
305 @end
306
307 @implementation WKView
308
309 #if WK_API_ENABLED
310
311 - (id)initWithFrame:(NSRect)frame processGroup:(WKProcessGroup *)processGroup browsingContextGroup:(WKBrowsingContextGroup *)browsingContextGroup
312 {
313     return [self initWithFrame:frame contextRef:processGroup._contextRef pageGroupRef:browsingContextGroup._pageGroupRef relatedToPage:nil];
314 }
315
316 - (id)initWithFrame:(NSRect)frame processGroup:(WKProcessGroup *)processGroup browsingContextGroup:(WKBrowsingContextGroup *)browsingContextGroup relatedToView:(WKView *)relatedView
317 {
318     return [self initWithFrame:frame contextRef:processGroup._contextRef pageGroupRef:browsingContextGroup._pageGroupRef relatedToPage:relatedView ? toAPI(relatedView->_data->_page.get()) : nil];
319 }
320
321 #endif // WK_API_ENABLED
322
323 - (void)dealloc
324 {
325 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
326     [_data->_actionMenuController willDestroyView:self];
327     [_data->_immediateActionController willDestroyView:self];
328 #endif
329
330     _data->_page->close();
331
332 #if WK_API_ENABLED
333     ASSERT(!_data->_thumbnailView);
334 #endif
335     ASSERT(!_data->_inSecureInputState);
336
337     [_data release];
338     _data = nil;
339
340     NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
341     [workspaceNotificationCenter removeObserver:self name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
342
343     if (canLoadLUNotificationPopoverWillClose())
344         [[NSNotificationCenter defaultCenter] removeObserver:self name:getLUNotificationPopoverWillClose() object:nil];
345
346     WebProcessPool::statistics().wkViewCount--;
347
348     [super dealloc];
349 }
350
351 #if WK_API_ENABLED
352
353 - (WKBrowsingContextController *)browsingContextController
354 {
355     if (!_data->_browsingContextController)
356         _data->_browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(_data->_page.get())]);
357
358     return _data->_browsingContextController.get();
359 }
360
361 #endif // WK_API_ENABLED
362
363 - (void)setDrawsBackground:(BOOL)drawsBackground
364 {
365     _data->_page->setDrawsBackground(drawsBackground);
366 }
367
368 - (BOOL)drawsBackground
369 {
370     return _data->_page->drawsBackground();
371 }
372
373 - (void)setDrawsTransparentBackground:(BOOL)drawsTransparentBackground
374 {
375     _data->_page->setDrawsTransparentBackground(drawsTransparentBackground);
376 }
377
378 - (BOOL)drawsTransparentBackground
379 {
380     return _data->_page->drawsTransparentBackground();
381 }
382
383 - (BOOL)acceptsFirstResponder
384 {
385     return YES;
386 }
387
388 - (BOOL)becomeFirstResponder
389 {
390     NSSelectionDirection direction = [[self window] keyViewSelectionDirection];
391
392     _data->_inBecomeFirstResponder = true;
393     
394     [self _updateSecureInputState];
395     _data->_page->viewStateDidChange(ViewState::IsFocused);
396
397     _data->_inBecomeFirstResponder = false;
398     
399     if (direction != NSDirectSelection) {
400         NSEvent *event = [NSApp currentEvent];
401         NSEvent *keyboardEvent = nil;
402         if ([event type] == NSKeyDown || [event type] == NSKeyUp)
403             keyboardEvent = event;
404         _data->_page->setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, Vector<KeypressCommand>()));
405     }
406     return YES;
407 }
408
409 - (BOOL)resignFirstResponder
410 {
411     _data->_inResignFirstResponder = true;
412
413 #if USE(ASYNC_NSTEXTINPUTCLIENT)
414     _data->_page->confirmCompositionAsync();
415 #else
416     if (_data->_page->editorState().hasComposition && !_data->_page->editorState().shouldIgnoreCompositionSelectionChange)
417         _data->_page->cancelComposition();
418 #endif
419
420     [self _notifyInputContextAboutDiscardedComposition];
421
422     [self _resetSecureInputState];
423
424     if (!_data->_page->maintainsInactiveSelection())
425         _data->_page->clearSelection();
426     
427     _data->_page->viewStateDidChange(ViewState::IsFocused);
428
429     _data->_inResignFirstResponder = false;
430
431     return YES;
432 }
433
434 - (void)viewWillStartLiveResize
435 {
436     _data->_page->viewWillStartLiveResize();
437 }
438
439 - (void)viewDidEndLiveResize
440 {
441     _data->_page->viewWillEndLiveResize();
442 }
443
444 - (BOOL)isFlipped
445 {
446     return YES;
447 }
448
449 - (NSSize)intrinsicContentSize
450 {
451     return _data->_intrinsicContentSize;
452 }
453
454 - (void)prepareContentInRect:(NSRect)rect
455 {
456     _data->_contentPreparationRect = rect;
457     _data->_useContentPreparationRectForVisibleRect = YES;
458
459     [self _updateViewExposedRect];
460 }
461
462 - (void)_updateViewExposedRect
463 {
464     NSRect exposedRect = [self visibleRect];
465
466     if (_data->_useContentPreparationRectForVisibleRect)
467         exposedRect = NSUnionRect(_data->_contentPreparationRect, exposedRect);
468
469     if (auto drawingArea = _data->_page->drawingArea())
470         drawingArea->setExposedRect(_data->_clipsToVisibleRect ? FloatRect(exposedRect) : FloatRect::infiniteRect());
471 }
472
473 - (void)setFrameSize:(NSSize)size
474 {
475     [super setFrameSize:size];
476
477     if (![self frameSizeUpdatesDisabled]) {
478         if (_data->_clipsToVisibleRect)
479             [self _updateViewExposedRect];
480         [self _setDrawingAreaSize:size];
481     }
482 }
483
484 - (void)_updateWindowAndViewFrames
485 {
486     if (_data->_clipsToVisibleRect)
487         [self _updateViewExposedRect];
488
489     if (_data->_didScheduleWindowAndViewFrameUpdate)
490         return;
491
492     _data->_didScheduleWindowAndViewFrameUpdate = YES;
493
494     dispatch_async(dispatch_get_main_queue(), ^{
495         _data->_didScheduleWindowAndViewFrameUpdate = NO;
496
497         NSRect viewFrameInWindowCoordinates = NSZeroRect;
498         NSPoint accessibilityPosition = NSZeroPoint;
499
500         if (_data->_needsViewFrameInWindowCoordinates)
501             viewFrameInWindowCoordinates = [self convertRect:self.frame toView:nil];
502
503 #pragma clang diagnostic push
504 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
505         if (WebCore::AXObjectCache::accessibilityEnabled())
506             accessibilityPosition = [[self accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
507 #pragma clang diagnostic pop
508
509         _data->_page->windowAndViewFramesChanged(viewFrameInWindowCoordinates, accessibilityPosition);
510     });
511 }
512
513 - (void)renewGState
514 {
515     // Hide the find indicator.
516     _data->_textIndicatorWindow = nullptr;
517
518     // Update the view frame.
519     if ([self window])
520         [self _updateWindowAndViewFrames];
521
522 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
523     [self _updateContentInsetsIfAutomatic];
524 #endif
525
526     [super renewGState];
527 }
528
529 - (void)_setPluginComplexTextInputState:(PluginComplexTextInputState)pluginComplexTextInputState
530 {
531     _data->_pluginComplexTextInputState = pluginComplexTextInputState;
532     
533     if (_data->_pluginComplexTextInputState != PluginComplexTextInputDisabled)
534         return;
535
536     // Send back an empty string to the plug-in. This will disable text input.
537     _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, String());
538 }
539
540 typedef HashMap<SEL, String> SelectorNameMap;
541
542 // Map selectors into Editor command names.
543 // This is not needed for any selectors that have the same name as the Editor command.
544 static const SelectorNameMap* createSelectorExceptionMap()
545 {
546     SelectorNameMap* map = new HashMap<SEL, String>;
547     
548     map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline");
549     map->add(@selector(insertParagraphSeparator:), "InsertNewline");
550     map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab");
551     map->add(@selector(pageDown:), "MovePageDown");
552     map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection");
553     map->add(@selector(pageUp:), "MovePageUp");
554     map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection");
555     map->add(@selector(scrollPageDown:), "ScrollPageForward");
556     map->add(@selector(scrollPageUp:), "ScrollPageBackward");
557     
558     return map;
559 }
560
561 static String commandNameForSelector(SEL selector)
562 {
563     // Check the exception map first.
564     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
565     SelectorNameMap::const_iterator it = exceptionMap->find(selector);
566     if (it != exceptionMap->end())
567         return it->value;
568     
569     // Remove the trailing colon.
570     // No need to capitalize the command name since Editor command names are
571     // not case sensitive.
572     const char* selectorName = sel_getName(selector);
573     size_t selectorNameLength = strlen(selectorName);
574     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
575         return String();
576     return String(selectorName, selectorNameLength - 1);
577 }
578
579 // Editing commands
580
581 #define WEBCORE_COMMAND(command) - (void)command:(id)sender { _data->_page->executeEditCommand(commandNameForSelector(_cmd)); }
582
583 WEBCORE_COMMAND(alignCenter)
584 WEBCORE_COMMAND(alignJustified)
585 WEBCORE_COMMAND(alignLeft)
586 WEBCORE_COMMAND(alignRight)
587 WEBCORE_COMMAND(copy)
588 WEBCORE_COMMAND(cut)
589 WEBCORE_COMMAND(delete)
590 WEBCORE_COMMAND(deleteBackward)
591 WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter)
592 WEBCORE_COMMAND(deleteForward)
593 WEBCORE_COMMAND(deleteToBeginningOfLine)
594 WEBCORE_COMMAND(deleteToBeginningOfParagraph)
595 WEBCORE_COMMAND(deleteToEndOfLine)
596 WEBCORE_COMMAND(deleteToEndOfParagraph)
597 WEBCORE_COMMAND(deleteToMark)
598 WEBCORE_COMMAND(deleteWordBackward)
599 WEBCORE_COMMAND(deleteWordForward)
600 WEBCORE_COMMAND(ignoreSpelling)
601 WEBCORE_COMMAND(indent)
602 WEBCORE_COMMAND(insertBacktab)
603 WEBCORE_COMMAND(insertLineBreak)
604 WEBCORE_COMMAND(insertNewline)
605 WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor)
606 WEBCORE_COMMAND(insertParagraphSeparator)
607 WEBCORE_COMMAND(insertTab)
608 WEBCORE_COMMAND(insertTabIgnoringFieldEditor)
609 WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight)
610 WEBCORE_COMMAND(makeTextWritingDirectionNatural)
611 WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft)
612 WEBCORE_COMMAND(moveBackward)
613 WEBCORE_COMMAND(moveBackwardAndModifySelection)
614 WEBCORE_COMMAND(moveDown)
615 WEBCORE_COMMAND(moveDownAndModifySelection)
616 WEBCORE_COMMAND(moveForward)
617 WEBCORE_COMMAND(moveForwardAndModifySelection)
618 WEBCORE_COMMAND(moveLeft)
619 WEBCORE_COMMAND(moveLeftAndModifySelection)
620 WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection)
621 WEBCORE_COMMAND(moveParagraphForwardAndModifySelection)
622 WEBCORE_COMMAND(moveRight)
623 WEBCORE_COMMAND(moveRightAndModifySelection)
624 WEBCORE_COMMAND(moveToBeginningOfDocument)
625 WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection)
626 WEBCORE_COMMAND(moveToBeginningOfLine)
627 WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection)
628 WEBCORE_COMMAND(moveToBeginningOfParagraph)
629 WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection)
630 WEBCORE_COMMAND(moveToBeginningOfSentence)
631 WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection)
632 WEBCORE_COMMAND(moveToEndOfDocument)
633 WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection)
634 WEBCORE_COMMAND(moveToEndOfLine)
635 WEBCORE_COMMAND(moveToEndOfLineAndModifySelection)
636 WEBCORE_COMMAND(moveToEndOfParagraph)
637 WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection)
638 WEBCORE_COMMAND(moveToEndOfSentence)
639 WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection)
640 WEBCORE_COMMAND(moveToLeftEndOfLine)
641 WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection)
642 WEBCORE_COMMAND(moveToRightEndOfLine)
643 WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection)
644 WEBCORE_COMMAND(moveUp)
645 WEBCORE_COMMAND(moveUpAndModifySelection)
646 WEBCORE_COMMAND(moveWordBackward)
647 WEBCORE_COMMAND(moveWordBackwardAndModifySelection)
648 WEBCORE_COMMAND(moveWordForward)
649 WEBCORE_COMMAND(moveWordForwardAndModifySelection)
650 WEBCORE_COMMAND(moveWordLeft)
651 WEBCORE_COMMAND(moveWordLeftAndModifySelection)
652 WEBCORE_COMMAND(moveWordRight)
653 WEBCORE_COMMAND(moveWordRightAndModifySelection)
654 WEBCORE_COMMAND(outdent)
655 WEBCORE_COMMAND(pageDown)
656 WEBCORE_COMMAND(pageDownAndModifySelection)
657 WEBCORE_COMMAND(pageUp)
658 WEBCORE_COMMAND(pageUpAndModifySelection)
659 WEBCORE_COMMAND(paste)
660 WEBCORE_COMMAND(pasteAsPlainText)
661 WEBCORE_COMMAND(scrollPageDown)
662 WEBCORE_COMMAND(scrollPageUp)
663 WEBCORE_COMMAND(scrollLineDown)
664 WEBCORE_COMMAND(scrollLineUp)
665 WEBCORE_COMMAND(scrollToBeginningOfDocument)
666 WEBCORE_COMMAND(scrollToEndOfDocument)
667 WEBCORE_COMMAND(selectAll)
668 WEBCORE_COMMAND(selectLine)
669 WEBCORE_COMMAND(selectParagraph)
670 WEBCORE_COMMAND(selectSentence)
671 WEBCORE_COMMAND(selectToMark)
672 WEBCORE_COMMAND(selectWord)
673 WEBCORE_COMMAND(setMark)
674 WEBCORE_COMMAND(subscript)
675 WEBCORE_COMMAND(superscript)
676 WEBCORE_COMMAND(swapWithMark)
677 WEBCORE_COMMAND(takeFindStringFromSelection)
678 WEBCORE_COMMAND(transpose)
679 WEBCORE_COMMAND(underline)
680 WEBCORE_COMMAND(unscript)
681 WEBCORE_COMMAND(yank)
682 WEBCORE_COMMAND(yankAndSelect)
683
684 #undef WEBCORE_COMMAND
685
686 // This method is needed to support Mac OS X services.
687
688 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types
689 {
690     size_t numTypes = [types count];
691     [pasteboard declareTypes:types owner:nil];
692     for (size_t i = 0; i < numTypes; ++i) {
693         if ([[types objectAtIndex:i] isEqualTo:NSStringPboardType])
694             [pasteboard setString:_data->_page->stringSelectionForPasteboard() forType:NSStringPboardType];
695         else {
696             RefPtr<SharedBuffer> buffer = _data->_page->dataSelectionForPasteboard([types objectAtIndex:i]);
697             [pasteboard setData:buffer ? buffer->createNSData().get() : nil forType:[types objectAtIndex:i]];
698        }
699     }
700     return YES;
701 }
702
703 - (void)centerSelectionInVisibleArea:(id)sender 
704
705     _data->_page->centerSelectionInVisibleArea();
706 }
707
708 // This method is needed to support Mac OS X services.
709
710 - (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType
711 {
712     EditorState editorState = _data->_page->editorState();
713     BOOL isValidSendType = NO;
714
715     if (sendType && !editorState.selectionIsNone) {
716         if (editorState.isInPlugin)
717             isValidSendType = [sendType isEqualToString:NSStringPboardType];
718         else
719             isValidSendType = [PasteboardTypes::forSelection() containsObject:sendType];
720     }
721
722     BOOL isValidReturnType = NO;
723     if (!returnType)
724         isValidReturnType = YES;
725     else if ([PasteboardTypes::forEditing() containsObject:returnType] && editorState.isContentEditable) {
726         // We can insert strings in any editable context.  We can insert other types, like images, only in rich edit contexts.
727         isValidReturnType = editorState.isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType];
728     }
729     if (isValidSendType && isValidReturnType)
730         return self;
731     return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType];
732 }
733
734 // This method is needed to support Mac OS X services.
735
736 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard 
737 {
738     return _data->_page->readSelectionFromPasteboard([pasteboard name]);
739 }
740
741 // Font panel support.
742
743 - (void)updateFontPanelIfNeeded
744 {
745     const EditorState& editorState = _data->_page->editorState();
746     if (editorState.selectionIsNone || !editorState.isContentEditable)
747         return;
748     if ([NSFontPanel sharedFontPanelExists] && [[NSFontPanel sharedFontPanel] isVisible]) {
749         _data->_page->fontAtSelection([](const String& fontName, double fontSize, bool selectionHasMultipleFonts, CallbackBase::Error error) {
750             NSFont *font = [NSFont fontWithName:fontName size:fontSize];
751             if (font)
752                 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:selectionHasMultipleFonts];
753         });
754     }
755 }
756
757 - (void)_selectionChanged
758 {
759     [self updateFontPanelIfNeeded];
760 }
761
762 - (void)changeFont:(id)sender
763 {
764     NSFontManager *fontManager = [NSFontManager sharedFontManager];
765     NSFont *font = [fontManager convertFont:[fontManager selectedFont]];
766     if (!font)
767         return;
768     _data->_page->setFont([font familyName], [font pointSize], [[font fontDescriptor] symbolicTraits]);
769 }
770
771 /*
772
773 When possible, editing-related methods should be implemented in WebCore with the
774 EditorCommand mechanism and invoked via WEBCORE_COMMAND, rather than implementing
775 individual methods here with Mac-specific code.
776
777 Editing-related methods still unimplemented that are implemented in WebKit1:
778
779 - (void)complete:(id)sender;
780 - (void)copyFont:(id)sender;
781 - (void)makeBaseWritingDirectionLeftToRight:(id)sender;
782 - (void)makeBaseWritingDirectionNatural:(id)sender;
783 - (void)makeBaseWritingDirectionRightToLeft:(id)sender;
784 - (void)pasteFont:(id)sender;
785 - (void)scrollLineDown:(id)sender;
786 - (void)scrollLineUp:(id)sender;
787 - (void)showGuessPanel:(id)sender;
788
789 Some other editing-related methods still unimplemented:
790
791 - (void)changeCaseOfLetter:(id)sender;
792 - (void)copyRuler:(id)sender;
793 - (void)insertContainerBreak:(id)sender;
794 - (void)insertDoubleQuoteIgnoringSubstitution:(id)sender;
795 - (void)insertSingleQuoteIgnoringSubstitution:(id)sender;
796 - (void)pasteRuler:(id)sender;
797 - (void)toggleRuler:(id)sender;
798 - (void)transposeWords:(id)sender;
799
800 */
801
802 // Menu items validation
803
804 static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item)
805 {
806     if (![(NSObject *)item isKindOfClass:[NSMenuItem class]])
807         return nil;
808     return (NSMenuItem *)item;
809 }
810
811 static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item)
812 {
813     if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]])
814         return nil;
815     return (NSToolbarItem *)item;
816 }
817
818 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
819 {
820     SEL action = [item action];
821
822     if (action == @selector(showGuessPanel:)) {
823         if (NSMenuItem *menuItem = ::menuItem(item))
824             [menuItem setTitle:contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])];
825         return _data->_page->editorState().isContentEditable;
826     }
827
828     if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:))
829         return _data->_page->editorState().isContentEditable;
830
831     if (action == @selector(toggleContinuousSpellChecking:)) {
832         bool enabled = TextChecker::isContinuousSpellCheckingAllowed();
833         bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled;
834         [menuItem(item) setState:checked ? NSOnState : NSOffState];
835         return enabled;
836     }
837
838     if (action == @selector(toggleGrammarChecking:)) {
839         bool checked = TextChecker::state().isGrammarCheckingEnabled;
840         [menuItem(item) setState:checked ? NSOnState : NSOffState];
841         return YES;
842     }
843
844     if (action == @selector(toggleAutomaticSpellingCorrection:)) {
845         bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled;
846         [menuItem(item) setState:checked ? NSOnState : NSOffState];
847         return _data->_page->editorState().isContentEditable;
848     }
849
850     if (action == @selector(orderFrontSubstitutionsPanel:)) {
851         if (NSMenuItem *menuItem = ::menuItem(item))
852             [menuItem setTitle:contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])];
853         return _data->_page->editorState().isContentEditable;
854     }
855
856     if (action == @selector(toggleSmartInsertDelete:)) {
857         bool checked = _data->_page->isSmartInsertDeleteEnabled();
858         [menuItem(item) setState:checked ? NSOnState : NSOffState];
859         return _data->_page->editorState().isContentEditable;
860     }
861
862     if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
863         bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
864         [menuItem(item) setState:checked ? NSOnState : NSOffState];
865         return _data->_page->editorState().isContentEditable;
866     }
867
868     if (action == @selector(toggleAutomaticDashSubstitution:)) {
869         bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled;
870         [menuItem(item) setState:checked ? NSOnState : NSOffState];
871         return _data->_page->editorState().isContentEditable;
872     }
873
874     if (action == @selector(toggleAutomaticLinkDetection:)) {
875         bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled;
876         [menuItem(item) setState:checked ? NSOnState : NSOffState];
877         return _data->_page->editorState().isContentEditable;
878     }
879
880     if (action == @selector(toggleAutomaticTextReplacement:)) {
881         bool checked = TextChecker::state().isAutomaticTextReplacementEnabled;
882         [menuItem(item) setState:checked ? NSOnState : NSOffState];
883         return _data->_page->editorState().isContentEditable;
884     }
885
886     if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:))
887         return _data->_page->editorState().selectionIsRange && _data->_page->editorState().isContentEditable;
888     
889     if (action == @selector(stopSpeaking:))
890         return [NSApp isSpeaking];
891     
892     // The centerSelectionInVisibleArea: selector is enabled if there's a selection range or if there's an insertion point in an editable area.
893     if (action == @selector(centerSelectionInVisibleArea:))
894         return _data->_page->editorState().selectionIsRange || (_data->_page->editorState().isContentEditable && !_data->_page->editorState().selectionIsNone);
895
896     // Next, handle editor commands. Start by returning YES for anything that is not an editor command.
897     // Returning YES is the default thing to do in an AppKit validate method for any selector that is not recognized.
898     String commandName = commandNameForSelector([item action]);
899     if (!Editor::commandIsSupportedFromMenuOrKeyBinding(commandName))
900         return YES;
901
902     // Add this item to the vector of items for a given command that are awaiting validation.
903     ValidationMap::AddResult addResult = _data->_validationMap.add(commandName, ValidationVector());
904     addResult.iterator->value.append(item);
905     if (addResult.isNewEntry) {
906         // If we are not already awaiting validation for this command, start the asynchronous validation process.
907         // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time
908         // we asked for the same command; there is no guarantee the answer is still valid.
909         _data->_page->validateCommand(commandName, [self](const String& commandName, bool isEnabled, int32_t state, CallbackBase::Error error) {
910             // If the process exits before the command can be validated, we'll be called back with an error.
911             if (error != CallbackBase::Error::None)
912                 return;
913             
914             [self _setUserInterfaceItemState:commandName enabled:isEnabled state:state];
915         });
916     }
917
918     // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called.
919     // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment.
920     // But returning NO here would be worse; that would make keyboard commands such as command-C fail.
921     return YES;
922 }
923
924 - (IBAction)startSpeaking:(id)sender
925 {
926     _data->_page->getSelectionOrContentsAsString([self](const String& string, CallbackBase::Error error) {
927         if (error != CallbackBase::Error::None)
928             return;
929         if (!string)
930             return;
931
932         [NSApp speakString:string];
933     });
934 }
935
936 - (IBAction)stopSpeaking:(id)sender
937 {
938     [NSApp stopSpeaking:sender];
939 }
940
941 - (IBAction)showGuessPanel:(id)sender
942 {
943     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
944     if (!checker) {
945         LOG_ERROR("No NSSpellChecker");
946         return;
947     }
948     
949     NSPanel *spellingPanel = [checker spellingPanel];
950     if ([spellingPanel isVisible]) {
951         [spellingPanel orderOut:sender];
952         return;
953     }
954     
955     _data->_page->advanceToNextMisspelling(true);
956     [spellingPanel orderFront:sender];
957 }
958
959 - (IBAction)checkSpelling:(id)sender
960 {
961     _data->_page->advanceToNextMisspelling(false);
962 }
963
964 - (void)changeSpelling:(id)sender
965 {
966     NSString *word = [[sender selectedCell] stringValue];
967
968     _data->_page->changeSpellingToWord(word);
969 }
970
971 - (IBAction)toggleContinuousSpellChecking:(id)sender
972 {
973     bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled;
974     TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled);
975
976     _data->_page->process().updateTextCheckerState();
977 }
978
979 - (BOOL)isGrammarCheckingEnabled
980 {
981     return TextChecker::state().isGrammarCheckingEnabled;
982 }
983
984 - (void)setGrammarCheckingEnabled:(BOOL)flag
985 {
986     if (static_cast<bool>(flag) == TextChecker::state().isGrammarCheckingEnabled)
987         return;
988     
989     TextChecker::setGrammarCheckingEnabled(flag);
990     _data->_page->process().updateTextCheckerState();
991 }
992
993 - (IBAction)toggleGrammarChecking:(id)sender
994 {
995     bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled;
996     TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled);
997
998     _data->_page->process().updateTextCheckerState();
999 }
1000
1001 - (IBAction)toggleAutomaticSpellingCorrection:(id)sender
1002 {
1003     TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled);
1004
1005     _data->_page->process().updateTextCheckerState();
1006 }
1007
1008 - (void)orderFrontSubstitutionsPanel:(id)sender
1009 {
1010     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
1011     if (!checker) {
1012         LOG_ERROR("No NSSpellChecker");
1013         return;
1014     }
1015     
1016     NSPanel *substitutionsPanel = [checker substitutionsPanel];
1017     if ([substitutionsPanel isVisible]) {
1018         [substitutionsPanel orderOut:sender];
1019         return;
1020     }
1021     [substitutionsPanel orderFront:sender];
1022 }
1023
1024 - (IBAction)toggleSmartInsertDelete:(id)sender
1025 {
1026     _data->_page->setSmartInsertDeleteEnabled(!_data->_page->isSmartInsertDeleteEnabled());
1027 }
1028
1029 - (BOOL)isAutomaticQuoteSubstitutionEnabled
1030 {
1031     return TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
1032 }
1033
1034 - (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag
1035 {
1036     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled)
1037         return;
1038
1039     TextChecker::setAutomaticQuoteSubstitutionEnabled(flag);
1040     _data->_page->process().updateTextCheckerState();
1041 }
1042
1043 - (void)toggleAutomaticQuoteSubstitution:(id)sender
1044 {
1045     TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled);
1046     _data->_page->process().updateTextCheckerState();
1047 }
1048
1049 - (BOOL)isAutomaticDashSubstitutionEnabled
1050 {
1051     return TextChecker::state().isAutomaticDashSubstitutionEnabled;
1052 }
1053
1054 - (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag
1055 {
1056     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled)
1057         return;
1058
1059     TextChecker::setAutomaticDashSubstitutionEnabled(flag);
1060     _data->_page->process().updateTextCheckerState();
1061 }
1062
1063 - (void)toggleAutomaticDashSubstitution:(id)sender
1064 {
1065     TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled);
1066     _data->_page->process().updateTextCheckerState();
1067 }
1068
1069 - (BOOL)isAutomaticLinkDetectionEnabled
1070 {
1071     return TextChecker::state().isAutomaticLinkDetectionEnabled;
1072 }
1073
1074 - (void)setAutomaticLinkDetectionEnabled:(BOOL)flag
1075 {
1076     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled)
1077         return;
1078
1079     TextChecker::setAutomaticLinkDetectionEnabled(flag);
1080     _data->_page->process().updateTextCheckerState();
1081 }
1082
1083 - (void)toggleAutomaticLinkDetection:(id)sender
1084 {
1085     TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled);
1086     _data->_page->process().updateTextCheckerState();
1087 }
1088
1089 - (BOOL)isAutomaticTextReplacementEnabled
1090 {
1091     return TextChecker::state().isAutomaticTextReplacementEnabled;
1092 }
1093
1094 - (void)setAutomaticTextReplacementEnabled:(BOOL)flag
1095 {
1096     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled)
1097         return;
1098
1099     TextChecker::setAutomaticTextReplacementEnabled(flag);
1100     _data->_page->process().updateTextCheckerState();
1101 }
1102
1103 - (void)toggleAutomaticTextReplacement:(id)sender
1104 {
1105     TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled);
1106     _data->_page->process().updateTextCheckerState();
1107 }
1108
1109 - (void)uppercaseWord:(id)sender
1110 {
1111     _data->_page->uppercaseWord();
1112 }
1113
1114 - (void)lowercaseWord:(id)sender
1115 {
1116     _data->_page->lowercaseWord();
1117 }
1118
1119 - (void)capitalizeWord:(id)sender
1120 {
1121     _data->_page->capitalizeWord();
1122 }
1123
1124 - (void)displayIfNeeded
1125 {
1126     // FIXME: We should remove this code when <rdar://problem/9362085> is resolved. In the meantime,
1127     // it is necessary to disable scren updates so we get a chance to redraw the corners before this 
1128     // display is visible.
1129     NSWindow *window = [self window];
1130     BOOL shouldMaskWindow = window && !NSIsEmptyRect(_data->_windowBottomCornerIntersectionRect);
1131     if (shouldMaskWindow)
1132         NSDisableScreenUpdates();
1133
1134     [super displayIfNeeded];
1135
1136     if (shouldMaskWindow) {
1137         [window _maskRoundedBottomCorners:_data->_windowBottomCornerIntersectionRect];
1138         NSEnableScreenUpdates();
1139         _data->_windowBottomCornerIntersectionRect = NSZeroRect;
1140     }
1141 }
1142
1143 // Events
1144
1145 // Override this so that AppKit will send us arrow keys as key down events so we can
1146 // support them via the key bindings mechanism.
1147 - (BOOL)_wantsKeyDownForEvent:(NSEvent *)event
1148 {
1149     return YES;
1150 }
1151
1152 - (void)_setMouseDownEvent:(NSEvent *)event
1153 {
1154     ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown);
1155     
1156     if (event == _data->_mouseDownEvent)
1157         return;
1158     
1159     [_data->_mouseDownEvent release];
1160     _data->_mouseDownEvent = [event retain];
1161 }
1162
1163 #if USE(ASYNC_NSTEXTINPUTCLIENT)
1164 #define NATIVE_MOUSE_EVENT_HANDLER(Selector) \
1165     - (void)Selector:(NSEvent *)theEvent \
1166     { \
1167         if (_data->_ignoresNonWheelEvents) \
1168             return; \
1169         if (NSTextInputContext *context = [self inputContext]) { \
1170             [context handleEvent:theEvent completionHandler:^(BOOL handled) { \
1171                 if (handled) \
1172                     LOG(TextInput, "%s was handled by text input context", String(#Selector).substring(0, String(#Selector).find("Internal")).ascii().data()); \
1173                 else { \
1174                     NativeWebMouseEvent webEvent(theEvent, self); \
1175                     _data->_page->handleMouseEvent(webEvent); \
1176                 } \
1177             }]; \
1178             return; \
1179         } \
1180         if ([NSMenu respondsToSelector:@selector(menuTypeForEvent:)] && [NSMenu menuTypeForEvent:theEvent] == NSMenuTypeActionMenu) { \
1181             [super Selector:theEvent]; \
1182             return; \
1183         } \
1184         NativeWebMouseEvent webEvent(theEvent, self); \
1185         _data->_page->handleMouseEvent(webEvent); \
1186     }
1187 #define NATIVE_MOUSE_EVENT_HANDLER_INTERNAL(Selector) \
1188     - (void)Selector:(NSEvent *)theEvent \
1189     { \
1190         if (_data->_ignoresNonWheelEvents) \
1191             return; \
1192         if (NSTextInputContext *context = [self inputContext]) { \
1193             [context handleEvent:theEvent completionHandler:^(BOOL handled) { \
1194                 if (handled) \
1195                     LOG(TextInput, "%s was handled by text input context", String(#Selector).substring(0, String(#Selector).find("Internal")).ascii().data()); \
1196                 else { \
1197                     NativeWebMouseEvent webEvent(theEvent, self); \
1198                     _data->_page->handleMouseEvent(webEvent); \
1199                 } \
1200             }]; \
1201             return; \
1202         } \
1203         NativeWebMouseEvent webEvent(theEvent, self); \
1204         _data->_page->handleMouseEvent(webEvent); \
1205     }
1206 #else
1207 #define NATIVE_MOUSE_EVENT_HANDLER(Selector) \
1208     - (void)Selector:(NSEvent *)theEvent \
1209     { \
1210         if (_data->_ignoresNonWheelEvents) \
1211             return; \
1212         if ([[self inputContext] handleEvent:theEvent]) { \
1213             LOG(TextInput, "%s was handled by text input context", String(#Selector).substring(0, String(#Selector).find("Internal")).ascii().data()); \
1214             return; \
1215         } \
1216         if ([NSMenu respondsToSelector:@selector(menuTypeForEvent:)] && [NSMenu menuTypeForEvent:theEvent] == NSMenuTypeActionMenu) { \
1217             [super Selector:theEvent]; \
1218             return; \
1219         } \
1220         NativeWebMouseEvent webEvent(theEvent, self); \
1221         _data->_page->handleMouseEvent(webEvent); \
1222     }
1223 #define NATIVE_MOUSE_EVENT_HANDLER_INTERNAL(Selector) \
1224     - (void)Selector:(NSEvent *)theEvent \
1225     { \
1226         if (_data->_ignoresNonWheelEvents) \
1227             return; \
1228         if ([[self inputContext] handleEvent:theEvent]) { \
1229             LOG(TextInput, "%s was handled by text input context", String(#Selector).substring(0, String(#Selector).find("Internal")).ascii().data()); \
1230             return; \
1231         } \
1232         NativeWebMouseEvent webEvent(theEvent, self); \
1233         _data->_page->handleMouseEvent(webEvent); \
1234     }
1235 #endif
1236
1237 NATIVE_MOUSE_EVENT_HANDLER(mouseEntered)
1238 NATIVE_MOUSE_EVENT_HANDLER(mouseExited)
1239 NATIVE_MOUSE_EVENT_HANDLER(otherMouseDown)
1240 NATIVE_MOUSE_EVENT_HANDLER(otherMouseDragged)
1241 NATIVE_MOUSE_EVENT_HANDLER(otherMouseUp)
1242 NATIVE_MOUSE_EVENT_HANDLER(rightMouseDown)
1243 NATIVE_MOUSE_EVENT_HANDLER(rightMouseDragged)
1244 NATIVE_MOUSE_EVENT_HANDLER(rightMouseUp)
1245
1246 NATIVE_MOUSE_EVENT_HANDLER_INTERNAL(mouseMovedInternal)
1247 NATIVE_MOUSE_EVENT_HANDLER_INTERNAL(mouseDownInternal)
1248 NATIVE_MOUSE_EVENT_HANDLER_INTERNAL(mouseUpInternal)
1249 NATIVE_MOUSE_EVENT_HANDLER_INTERNAL(mouseDraggedInternal)
1250
1251 #undef NATIVE_MOUSE_EVENT_HANDLER
1252
1253 - (void)_ensureGestureController
1254 {
1255     if (_data->_gestureController)
1256         return;
1257
1258     _data->_gestureController = std::make_unique<ViewGestureController>(*_data->_page);
1259 }
1260
1261 - (void)scrollWheel:(NSEvent *)event
1262 {
1263     if (_data->_ignoresAllEvents)
1264         return;
1265
1266     if (_data->_allowsBackForwardNavigationGestures) {
1267         [self _ensureGestureController];
1268         if (_data->_gestureController->handleScrollWheelEvent(event))
1269             return;
1270     }
1271
1272     NativeWebWheelEvent webEvent = NativeWebWheelEvent(event, self);
1273     _data->_page->handleWheelEvent(webEvent);
1274 }
1275
1276 - (void)swipeWithEvent:(NSEvent *)event
1277 {
1278     if (_data->_ignoresNonWheelEvents)
1279         return;
1280
1281     if (!_data->_allowsBackForwardNavigationGestures) {
1282         [super swipeWithEvent:event];
1283         return;
1284     }
1285
1286     if (event.deltaX > 0.0)
1287         _data->_page->goBack();
1288     else if (event.deltaX < 0.0)
1289         _data->_page->goForward();
1290     else
1291         [super swipeWithEvent:event];
1292 }
1293
1294 - (void)mouseMoved:(NSEvent *)event
1295 {
1296     if (_data->_ignoresNonWheelEvents)
1297         return;
1298
1299     // When a view is first responder, it gets mouse moved events even when the mouse is outside its visible rect.
1300     if (self == [[self window] firstResponder] && !NSPointInRect([self convertPoint:[event locationInWindow] fromView:nil], [self visibleRect]))
1301         return;
1302
1303     [self mouseMovedInternal:event];
1304 }
1305
1306 - (void)mouseDown:(NSEvent *)event
1307 {
1308     if (_data->_ignoresNonWheelEvents)
1309         return;
1310
1311     [self _setMouseDownEvent:event];
1312     _data->_ignoringMouseDraggedEvents = NO;
1313
1314     [self _dismissContentRelativeChildWindows];
1315 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1316     [_data->_actionMenuController wkView:self willHandleMouseDown:event];
1317 #endif
1318     [self mouseDownInternal:event];
1319 }
1320
1321 - (void)mouseUp:(NSEvent *)event
1322 {
1323     if (_data->_ignoresNonWheelEvents)
1324         return;
1325
1326     [self _setMouseDownEvent:nil];
1327     [self mouseUpInternal:event];
1328 }
1329
1330 - (void)mouseDragged:(NSEvent *)event
1331 {
1332     if (_data->_ignoresNonWheelEvents)
1333         return;
1334
1335     if (_data->_ignoringMouseDraggedEvents)
1336         return;
1337     [self mouseDraggedInternal:event];
1338 }
1339
1340 - (BOOL)acceptsFirstMouse:(NSEvent *)event
1341 {
1342     // There's a chance that responding to this event will run a nested event loop, and
1343     // fetching a new event might release the old one. Retaining and then autoreleasing
1344     // the current event prevents that from causing a problem inside WebKit or AppKit code.
1345     [[event retain] autorelease];
1346     
1347     if (![self hitTest:[event locationInWindow]])
1348         return NO;
1349     
1350     [self _setMouseDownEvent:event];
1351     bool result = _data->_page->acceptsFirstMouse([event eventNumber], WebEventFactory::createWebMouseEvent(event, self));
1352     [self _setMouseDownEvent:nil];
1353     return result;
1354 }
1355
1356 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event
1357 {
1358     // If this is the active window or we don't have a range selection, there is no need to perform additional checks
1359     // and we can avoid making a synchronous call to the WebProcess.
1360     if ([[self window] isKeyWindow] || _data->_page->editorState().selectionIsNone || !_data->_page->editorState().selectionIsRange)
1361         return NO;
1362
1363     // There's a chance that responding to this event will run a nested event loop, and
1364     // fetching a new event might release the old one. Retaining and then autoreleasing
1365     // the current event prevents that from causing a problem inside WebKit or AppKit code.
1366     [[event retain] autorelease];
1367     
1368     if (![self hitTest:[event locationInWindow]])
1369         return NO;
1370     
1371     [self _setMouseDownEvent:event];
1372     bool result = _data->_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, self));
1373     [self _setMouseDownEvent:nil];
1374     return result;
1375 }
1376
1377 - (void)_disableComplexTextInputIfNecessary
1378 {
1379     if (!_data->_pluginComplexTextInputIdentifier)
1380         return;
1381
1382     if (_data->_pluginComplexTextInputState != PluginComplexTextInputEnabled)
1383         return;
1384
1385     // Check if the text input window has been dismissed.
1386     if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
1387         [self _setPluginComplexTextInputState:PluginComplexTextInputDisabled];
1388 }
1389
1390 - (BOOL)_handlePluginComplexTextInputKeyDown:(NSEvent *)event
1391 {
1392     ASSERT(_data->_pluginComplexTextInputIdentifier);
1393     ASSERT(_data->_pluginComplexTextInputState != PluginComplexTextInputDisabled);
1394
1395     BOOL usingLegacyCocoaTextInput = _data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
1396
1397     NSString *string = nil;
1398     BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string];
1399
1400     if (string) {
1401         _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, string);
1402
1403         if (!usingLegacyCocoaTextInput)
1404             _data->_pluginComplexTextInputState = PluginComplexTextInputDisabled;
1405     }
1406
1407     return didHandleEvent;
1408 }
1409
1410 - (BOOL)_tryHandlePluginComplexTextInputKeyDown:(NSEvent *)event
1411 {
1412     if (!_data->_pluginComplexTextInputIdentifier || _data->_pluginComplexTextInputState == PluginComplexTextInputDisabled)
1413         return NO;
1414
1415     // Check if the text input window has been dismissed and let the plug-in process know.
1416     // This is only valid with the updated Cocoa text input spec.
1417     [self _disableComplexTextInputIfNecessary];
1418
1419     // Try feeding the keyboard event directly to the plug-in.
1420     if (_data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
1421         return [self _handlePluginComplexTextInputKeyDown:event];
1422
1423     return NO;
1424 }
1425
1426 static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result)
1427 {
1428     int length = [[string string] length];
1429     
1430     int i = 0;
1431     while (i < length) {
1432         NSRange range;
1433         NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
1434         
1435         if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
1436             Color color = Color::black;
1437             if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
1438                 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
1439             result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1));
1440         }
1441         
1442         i = range.location + range.length;
1443     }
1444 }
1445
1446 #if USE(ASYNC_NSTEXTINPUTCLIENT)
1447
1448 - (void)_collectKeyboardLayoutCommandsForEvent:(NSEvent *)event to:(Vector<KeypressCommand>&)commands
1449 {
1450     if ([event type] != NSKeyDown)
1451         return;
1452
1453     ASSERT(!_data->_collectedKeypressCommands);
1454     _data->_collectedKeypressCommands = &commands;
1455
1456     if (NSTextInputContext *context = [self inputContext])
1457         [context handleEventByKeyboardLayout:event];
1458     else
1459         [self interpretKeyEvents:[NSArray arrayWithObject:event]];
1460
1461     _data->_collectedKeypressCommands = nullptr;
1462 }
1463
1464 - (void)_interpretKeyEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled, const Vector<KeypressCommand>& commands))completionHandler
1465 {
1466     // For regular Web content, input methods run before passing a keydown to DOM, but plug-ins get an opportunity to handle the event first.
1467     // There is no need to collect commands, as the plug-in cannot execute them.
1468     if (_data->_pluginComplexTextInputIdentifier) {
1469         completionHandler(NO, Vector<KeypressCommand>());
1470         return;
1471     }
1472
1473     if (![self inputContext]) {
1474         Vector<KeypressCommand> commands;
1475         [self _collectKeyboardLayoutCommandsForEvent:event to:commands];
1476         completionHandler(NO, commands);
1477         return;
1478     }
1479
1480     LOG(TextInput, "-> handleEventByInputMethod:%p %@", event, event);
1481     [[self inputContext] handleEventByInputMethod:event completionHandler:^(BOOL handled) {
1482         
1483         LOG(TextInput, "... handleEventByInputMethod%s handled", handled ? "" : " not");
1484         if (handled) {
1485             completionHandler(YES, Vector<KeypressCommand>());
1486             return;
1487         }
1488
1489         Vector<KeypressCommand> commands;
1490         [self _collectKeyboardLayoutCommandsForEvent:event to:commands];
1491         completionHandler(NO, commands);
1492     }];
1493 }
1494
1495 - (void)doCommandBySelector:(SEL)selector
1496 {
1497     LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
1498
1499     Vector<KeypressCommand>* keypressCommands = _data->_collectedKeypressCommands;
1500
1501     if (keypressCommands) {
1502         KeypressCommand command(NSStringFromSelector(selector));
1503         keypressCommands->append(command);
1504         LOG(TextInput, "...stored");
1505         _data->_page->registerKeypressCommandName(command.commandName);
1506     } else {
1507         // FIXME: Send the command to Editor synchronously and only send it along the
1508         // responder chain if it's a selector that does not correspond to an editing command.
1509         [super doCommandBySelector:selector];
1510     }
1511 }
1512
1513 - (void)insertText:(id)string
1514 {
1515     // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
1516     // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
1517     // command ensures that a keypress event is dispatched as appropriate.
1518     [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
1519 }
1520
1521 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
1522 {
1523     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
1524     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
1525
1526     if (replacementRange.location != NSNotFound)
1527         LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
1528     else
1529         LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
1530
1531     NSString *text;
1532     Vector<TextAlternativeWithRange> dictationAlternatives;
1533
1534     bool registerUndoGroup = false;
1535     if (isAttributedString) {
1536 #if USE(DICTATION_ALTERNATIVES)
1537         collectDictationTextAlternatives(string, dictationAlternatives);
1538 #endif
1539 #if USE(INSERTION_UNDO_GROUPING)
1540         registerUndoGroup = shouldRegisterInsertionUndoGroup(string);
1541 #endif
1542         // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
1543         text = [string string];
1544     } else
1545         text = string;
1546
1547     // insertText can be called for several reasons:
1548     // - If it's from normal key event processing (including key bindings), we save the action to perform it later.
1549     // - If it's from an input method, then we should go ahead and insert the text now.
1550     // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
1551     // then we also execute it immediately, as there will be no other chance.
1552     Vector<KeypressCommand>* keypressCommands = _data->_collectedKeypressCommands;
1553     if (keypressCommands) {
1554         ASSERT(replacementRange.location == NSNotFound);
1555         KeypressCommand command("insertText:", text);
1556         keypressCommands->append(command);
1557         LOG(TextInput, "...stored");
1558         _data->_page->registerKeypressCommandName(command.commandName);
1559         return;
1560     }
1561
1562     String eventText = text;
1563     eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
1564     if (!dictationAlternatives.isEmpty())
1565         _data->_page->insertDictatedTextAsync(eventText, replacementRange, dictationAlternatives, registerUndoGroup);
1566     else
1567         _data->_page->insertTextAsync(eventText, replacementRange, registerUndoGroup);
1568 }
1569
1570 - (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr
1571 {
1572     RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
1573
1574     LOG(TextInput, "selectedRange");
1575     _data->_page->getSelectedRangeAsync([completionHandler](const EditingRange& editingRangeResult, CallbackBase::Error error) {
1576         void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
1577         if (error != CallbackBase::Error::None) {
1578             LOG(TextInput, "    ...selectedRange failed.");
1579             completionHandlerBlock(NSMakeRange(NSNotFound, 0));
1580             return;
1581         }
1582         NSRange result = editingRangeResult;
1583         if (result.location == NSNotFound)
1584             LOG(TextInput, "    -> selectedRange returned (NSNotFound, %llu)", result.length);
1585         else
1586             LOG(TextInput, "    -> selectedRange returned (%llu, %llu)", result.location, result.length);
1587         completionHandlerBlock(result);
1588     });
1589 }
1590
1591 - (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr
1592 {
1593     RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
1594
1595     LOG(TextInput, "markedRange");
1596     _data->_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, CallbackBase::Error error) {
1597         void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
1598         if (error != CallbackBase::Error::None) {
1599             LOG(TextInput, "    ...markedRange failed.");
1600             completionHandlerBlock(NSMakeRange(NSNotFound, 0));
1601             return;
1602         }
1603         NSRange result = editingRangeResult;
1604         if (result.location == NSNotFound)
1605             LOG(TextInput, "    -> markedRange returned (NSNotFound, %llu)", result.length);
1606         else
1607             LOG(TextInput, "    -> markedRange returned (%llu, %llu)", result.location, result.length);
1608         completionHandlerBlock(result);
1609     });
1610 }
1611
1612 - (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr
1613 {
1614     RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
1615
1616     LOG(TextInput, "hasMarkedText");
1617     _data->_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, CallbackBase::Error error) {
1618         void (^completionHandlerBlock)(BOOL) = (void (^)(BOOL))completionHandler.get();
1619         if (error != CallbackBase::Error::None) {
1620             LOG(TextInput, "    ...hasMarkedText failed.");
1621             completionHandlerBlock(NO);
1622             return;
1623         }
1624         BOOL hasMarkedText = editingRangeResult.location != notFound;
1625         LOG(TextInput, "    -> hasMarkedText returned %u", hasMarkedText);
1626         completionHandlerBlock(hasMarkedText);
1627     });
1628 }
1629
1630 - (void)attributedSubstringForProposedRange:(NSRange)nsRange completionHandler:(void(^)(NSAttributedString *attrString, NSRange actualRange))completionHandlerPtr
1631 {
1632     RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
1633
1634     LOG(TextInput, "attributedSubstringFromRange:(%u, %u)", nsRange.location, nsRange.length);
1635     _data->_page->attributedSubstringForCharacterRangeAsync(nsRange, [completionHandler](const AttributedString& string, const EditingRange& actualRange, CallbackBase::Error error) {
1636         void (^completionHandlerBlock)(NSAttributedString *, NSRange) = (void (^)(NSAttributedString *, NSRange))completionHandler.get();
1637         if (error != CallbackBase::Error::None) {
1638             LOG(TextInput, "    ...attributedSubstringFromRange failed.");
1639             completionHandlerBlock(0, NSMakeRange(NSNotFound, 0));
1640             return;
1641         }
1642         LOG(TextInput, "    -> attributedSubstringFromRange returned %@", [string.string.get() string]);
1643         completionHandlerBlock([[string.string.get() retain] autorelease], actualRange);
1644     });
1645 }
1646
1647 - (void)firstRectForCharacterRange:(NSRange)theRange completionHandler:(void(^)(NSRect firstRect, NSRange actualRange))completionHandlerPtr
1648 {
1649     RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
1650
1651     LOG(TextInput, "firstRectForCharacterRange:(%u, %u)", theRange.location, theRange.length);
1652
1653     // Just to match NSTextView's behavior. Regression tests cannot detect this;
1654     // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
1655     // (type something; try ranges (1, -1) and (2, -1).
1656     if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
1657         theRange.length = 0;
1658
1659     if (theRange.location == NSNotFound) {
1660         LOG(TextInput, "    -> NSZeroRect");
1661         completionHandlerPtr(NSZeroRect, theRange);
1662         return;
1663     }
1664
1665     _data->_page->firstRectForCharacterRangeAsync(theRange, [self, completionHandler](const IntRect& rect, const EditingRange& actualRange, CallbackBase::Error error) {
1666         void (^completionHandlerBlock)(NSRect, NSRange) = (void (^)(NSRect, NSRange))completionHandler.get();
1667         if (error != CallbackBase::Error::None) {
1668             LOG(TextInput, "    ...firstRectForCharacterRange failed.");
1669             completionHandlerBlock(NSZeroRect, NSMakeRange(NSNotFound, 0));
1670             return;
1671         }
1672
1673         NSRect resultRect = [self convertRect:rect toView:nil];
1674         resultRect = [self.window convertRectToScreen:resultRect];
1675
1676         LOG(TextInput, "    -> firstRectForCharacterRange returned (%f, %f, %f, %f)", resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
1677         completionHandlerBlock(resultRect, actualRange);
1678     });
1679 }
1680
1681 - (void)characterIndexForPoint:(NSPoint)thePoint completionHandler:(void(^)(NSUInteger))completionHandlerPtr
1682 {
1683     RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]);
1684
1685     LOG(TextInput, "characterIndexForPoint:(%f, %f)", thePoint.x, thePoint.y);
1686
1687     NSWindow *window = [self window];
1688
1689 #pragma clang diagnostic push
1690 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1691     if (window)
1692         thePoint = [window convertScreenToBase:thePoint];
1693 #pragma clang diagnostic pop
1694     thePoint = [self convertPoint:thePoint fromView:nil];  // the point is relative to the main frame
1695
1696     _data->_page->characterIndexForPointAsync(IntPoint(thePoint), [completionHandler](uint64_t result, CallbackBase::Error error) {
1697         void (^completionHandlerBlock)(NSUInteger) = (void (^)(NSUInteger))completionHandler.get();
1698         if (error != CallbackBase::Error::None) {
1699             LOG(TextInput, "    ...characterIndexForPoint failed.");
1700             completionHandlerBlock(0);
1701             return;
1702         }
1703         if (result == notFound)
1704             result = NSNotFound;
1705         LOG(TextInput, "    -> characterIndexForPoint returned %lu", result);
1706         completionHandlerBlock(result);
1707     });
1708 }
1709
1710 - (NSTextInputContext *)inputContext
1711 {
1712     if (_data->_pluginComplexTextInputIdentifier) {
1713         ASSERT(!_data->_collectedKeypressCommands); // Should not get here from -_interpretKeyEvent:completionHandler:, we only use WKTextInputWindowController after giving the plug-in a chance to handle keydown natively.
1714         return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
1715     }
1716
1717     // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
1718     if (!_data->_page->editorState().isContentEditable)
1719         return nil;
1720
1721     return [super inputContext];
1722 }
1723
1724 - (void)unmarkText
1725 {
1726     LOG(TextInput, "unmarkText");
1727
1728     _data->_page->confirmCompositionAsync();
1729 }
1730
1731 - (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
1732 {
1733     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
1734     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
1735
1736     LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u) replacementRange:(%u, %u)", isAttributedString ? [string string] : string, selectedRange.location, selectedRange.length, replacementRange.location, replacementRange.length);
1737
1738     Vector<CompositionUnderline> underlines;
1739     NSString *text;
1740
1741     if (isAttributedString) {
1742         // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
1743         text = [string string];
1744         extractUnderlines(string, underlines);
1745     } else
1746         text = string;
1747
1748     if (_data->_inSecureInputState) {
1749         // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
1750         // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
1751         ASSERT(!_data->_page->editorState().hasComposition);
1752         [self _notifyInputContextAboutDiscardedComposition];
1753         // FIXME: We should store the command to handle it after DOM event processing, as it's regular keyboard input now, not a composition.
1754         if ([text length] == 1 && isASCII([text characterAtIndex:0]))
1755             _data->_page->insertTextAsync(text, replacementRange);
1756         else
1757             NSBeep();
1758         return;
1759     }
1760
1761     _data->_page->setCompositionAsync(text, underlines, selectedRange, replacementRange);
1762 }
1763
1764 // Synchronous NSTextInputClient is still implemented to catch spurious sync calls. Remove when that is no longer needed.
1765
1766 - (NSRange)selectedRange NO_RETURN_DUE_TO_ASSERT
1767 {
1768     ASSERT_NOT_REACHED();
1769     return NSMakeRange(NSNotFound, 0);
1770 }
1771
1772 - (BOOL)hasMarkedText NO_RETURN_DUE_TO_ASSERT
1773 {
1774     ASSERT_NOT_REACHED();
1775     return NO;
1776 }
1777
1778 - (NSRange)markedRange NO_RETURN_DUE_TO_ASSERT
1779 {
1780     ASSERT_NOT_REACHED();
1781     return NSMakeRange(NSNotFound, 0);
1782 }
1783
1784 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT
1785 {
1786     ASSERT_NOT_REACHED();
1787     return nil;
1788 }
1789
1790 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint NO_RETURN_DUE_TO_ASSERT
1791 {
1792     ASSERT_NOT_REACHED();
1793     return 0;
1794 }
1795
1796 - (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT
1797
1798     ASSERT_NOT_REACHED();
1799     return NSMakeRect(0, 0, 0, 0);
1800 }
1801
1802 - (BOOL)performKeyEquivalent:(NSEvent *)event
1803 {
1804     if (_data->_ignoresNonWheelEvents)
1805         return NO;
1806
1807     // There's a chance that responding to this event will run a nested event loop, and
1808     // fetching a new event might release the old one. Retaining and then autoreleasing
1809     // the current event prevents that from causing a problem inside WebKit or AppKit code.
1810     [[event retain] autorelease];
1811
1812     // We get Esc key here after processing either Esc or Cmd+period. The former starts as a keyDown, and the latter starts as a key equivalent,
1813     // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
1814     // Don't interpret this event again, avoiding re-entrancy and infinite loops.
1815     if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask))
1816         return [super performKeyEquivalent:event];
1817
1818     if (_data->_keyDownEventBeingResent) {
1819         // WebCore has already seen the event, no need for custom processing.
1820         // Note that we can get multiple events for each event being re-sent. For example, for Cmd+'=' AppKit
1821         // first performs the original key equivalent, and if that isn't handled, it dispatches a synthetic Cmd+'+'.
1822         return [super performKeyEquivalent:event];
1823     }
1824
1825     ASSERT(event == [NSApp currentEvent]);
1826
1827     [self _disableComplexTextInputIfNecessary];
1828
1829     // Pass key combos through WebCore if there is a key binding available for
1830     // this event. This lets webpages have a crack at intercepting key-modified keypresses.
1831     // FIXME: Why is the firstResponder check needed?
1832     if (self == [[self window] firstResponder]) {
1833         [self _interpretKeyEvent:event completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
1834             _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
1835         }];
1836         return YES;
1837     }
1838     
1839     return [super performKeyEquivalent:event];
1840 }
1841
1842 - (void)keyUp:(NSEvent *)theEvent
1843 {
1844     if (_data->_ignoresNonWheelEvents)
1845         return;
1846
1847     LOG(TextInput, "keyUp:%p %@", theEvent, theEvent);
1848
1849     [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
1850         ASSERT(!handledByInputMethod || commands.isEmpty());
1851         _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
1852     }];
1853 }
1854
1855 - (void)keyDown:(NSEvent *)theEvent
1856 {
1857     if (_data->_ignoresNonWheelEvents)
1858         return;
1859
1860     LOG(TextInput, "keyDown:%p %@%s", theEvent, theEvent, (theEvent == _data->_keyDownEventBeingResent) ? " (re-sent)" : "");
1861
1862     if ([self _tryHandlePluginComplexTextInputKeyDown:theEvent]) {
1863         LOG(TextInput, "...handled by plug-in");
1864         return;
1865     }
1866
1867     // We could be receiving a key down from AppKit if we have re-sent an event
1868     // that maps to an action that is currently unavailable (for example a copy when
1869     // there is no range selection).
1870     // If this is the case we should ignore the key down.
1871     if (_data->_keyDownEventBeingResent == theEvent) {
1872         [super keyDown:theEvent];
1873         return;
1874     }
1875
1876     [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
1877         ASSERT(!handledByInputMethod || commands.isEmpty());
1878         _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
1879     }];
1880 }
1881
1882 - (void)flagsChanged:(NSEvent *)theEvent
1883 {
1884     if (_data->_ignoresNonWheelEvents)
1885         return;
1886
1887     LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent);
1888
1889     unsigned short keyCode = [theEvent keyCode];
1890
1891     // Don't make an event from the num lock and function keys
1892     if (!keyCode || keyCode == 10 || keyCode == 63)
1893         return;
1894
1895     [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) {
1896         _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
1897     }];
1898 }
1899
1900 #else // USE(ASYNC_NSTEXTINPUTCLIENT)
1901
1902 - (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands
1903 {
1904     ASSERT(!_data->_interpretKeyEventsParameters);
1905     ASSERT(commands.isEmpty());
1906
1907     if ([event type] == NSFlagsChanged)
1908         return NO;
1909
1910     WKViewInterpretKeyEventsParameters parameters;
1911     parameters.eventInterpretationHadSideEffects = false;
1912     parameters.executingSavedKeypressCommands = false;
1913     // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called.
1914     // We assume the IM will *not* consume hotkey sequences.
1915     parameters.consumedByIM = !([event modifierFlags] & NSCommandKeyMask);
1916     parameters.commands = &commands;
1917     _data->_interpretKeyEventsParameters = &parameters;
1918
1919     LOG(TextInput, "-> interpretKeyEvents:%p %@", event, event);
1920     [self interpretKeyEvents:[NSArray arrayWithObject:event]];
1921
1922     _data->_interpretKeyEventsParameters = nullptr;
1923
1924     // An input method may consume an event and not tell us (e.g. when displaying a candidate window),
1925     // in which case we should not bubble the event up the DOM.
1926     if (parameters.consumedByIM) {
1927         ASSERT(commands.isEmpty());
1928         LOG(TextInput, "...event %p was consumed by an input method", event);
1929         return YES;
1930     }
1931
1932     LOG(TextInput, "...interpretKeyEvents for event %p done, returns %d", event, parameters.eventInterpretationHadSideEffects);
1933
1934     // If we have already executed all or some of the commands, the event is "handled". Note that there are additional checks on web process side.
1935     return parameters.eventInterpretationHadSideEffects;
1936 }
1937
1938 - (void)_executeSavedKeypressCommands
1939 {
1940     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
1941     if (!parameters || parameters->commands->isEmpty())
1942         return;
1943
1944     // We could be called again if the execution of one command triggers a call to selectedRange.
1945     // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result.
1946     if (parameters->executingSavedKeypressCommands)
1947         return;
1948
1949     LOG(TextInput, "Executing %u saved keypress commands...", parameters->commands->size());
1950
1951     parameters->executingSavedKeypressCommands = true;
1952     parameters->eventInterpretationHadSideEffects |= _data->_page->executeKeypressCommands(*parameters->commands);
1953     parameters->commands->clear();
1954     parameters->executingSavedKeypressCommands = false;
1955
1956     LOG(TextInput, "...done executing saved keypress commands.");
1957 }
1958
1959 - (void)doCommandBySelector:(SEL)selector
1960 {
1961     LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
1962
1963     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
1964     if (parameters)
1965         parameters->consumedByIM = false;
1966
1967     // As in insertText:replacementRange:, we assume that the call comes from an input method if there is marked text.
1968     bool isFromInputMethod = _data->_page->editorState().hasComposition;
1969
1970     if (parameters && !isFromInputMethod) {
1971         KeypressCommand command(NSStringFromSelector(selector));
1972         parameters->commands->append(command);
1973         LOG(TextInput, "...stored");
1974         _data->_page->registerKeypressCommandName(command.commandName);
1975     } else {
1976         // FIXME: Send the command to Editor synchronously and only send it along the
1977         // responder chain if it's a selector that does not correspond to an editing command.
1978         [super doCommandBySelector:selector];
1979     }
1980 }
1981
1982 - (void)insertText:(id)string
1983 {
1984     // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
1985     // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
1986     // command ensures that a keypress event is dispatched as appropriate.
1987     [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)];
1988 }
1989
1990 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
1991 {
1992     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
1993     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
1994
1995     if (replacementRange.location != NSNotFound)
1996         LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
1997     else
1998         LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
1999     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
2000     if (parameters)
2001         parameters->consumedByIM = false;
2002
2003     NSString *text;
2004     bool isFromInputMethod = _data->_page->editorState().hasComposition;
2005
2006     Vector<TextAlternativeWithRange> dictationAlternatives;
2007
2008     if (isAttributedString) {
2009 #if USE(DICTATION_ALTERNATIVES)
2010         collectDictationTextAlternatives(string, dictationAlternatives);
2011 #endif
2012         // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
2013         text = [string string];
2014     } else
2015         text = string;
2016
2017     // insertText can be called for several reasons:
2018     // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later.
2019     // - If it's from an input method, then we should go ahead and insert the text now. We assume it's from the input method if we have marked text.
2020     // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method.
2021     // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
2022     // then we also execute it immediately, as there will be no other chance.
2023     if (parameters && !isFromInputMethod) {
2024         // FIXME: Handle replacementRange in this case, too. It's known to occur in practice when canceling Press and Hold (see <rdar://11940670>).
2025         ASSERT(replacementRange.location == NSNotFound);
2026         KeypressCommand command("insertText:", text);
2027         parameters->commands->append(command);
2028         _data->_page->registerKeypressCommandName(command.commandName);
2029         return;
2030     }
2031
2032     String eventText = text;
2033     eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
2034     bool eventHandled;
2035     if (!dictationAlternatives.isEmpty())
2036         eventHandled = _data->_page->insertDictatedText(eventText, replacementRange, dictationAlternatives);
2037     else
2038         eventHandled = _data->_page->insertText(eventText, replacementRange);
2039
2040     if (parameters)
2041         parameters->eventInterpretationHadSideEffects |= eventHandled;
2042 }
2043
2044 - (NSTextInputContext *)inputContext
2045 {
2046     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
2047
2048     if (_data->_pluginComplexTextInputIdentifier && !parameters)
2049         return [[WKTextInputWindowController sharedTextInputWindowController] inputContext];
2050
2051     // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working.
2052     if (!_data->_page->editorState().isContentEditable)
2053         return nil;
2054
2055     return [super inputContext];
2056 }
2057
2058 - (NSRange)selectedRange
2059 {
2060     [self _executeSavedKeypressCommands];
2061
2062     EditingRange selectedRange;
2063     _data->_page->getSelectedRange(selectedRange);
2064
2065     NSRange result = selectedRange;
2066     if (result.location == NSNotFound)
2067         LOG(TextInput, "selectedRange -> (NSNotFound, %u)", result.length);
2068     else
2069         LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length);
2070
2071     return result;
2072 }
2073
2074 - (BOOL)hasMarkedText
2075 {
2076     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
2077
2078     BOOL result;
2079     if (parameters) {
2080         result = _data->_page->editorState().hasComposition;
2081         if (result) {
2082             // A saved command can confirm a composition, but it cannot start a new one.
2083             [self _executeSavedKeypressCommands];
2084             result = _data->_page->editorState().hasComposition;
2085         }
2086     } else {
2087         EditingRange markedRange;
2088         _data->_page->getMarkedRange(markedRange);
2089         result = markedRange.location != notFound;
2090     }
2091
2092     LOG(TextInput, "hasMarkedText -> %u", result);
2093     return result;
2094 }
2095
2096 - (void)unmarkText
2097 {
2098     [self _executeSavedKeypressCommands];
2099
2100     LOG(TextInput, "unmarkText");
2101
2102     // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
2103     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
2104
2105     if (parameters) {
2106         parameters->eventInterpretationHadSideEffects = true;
2107         parameters->consumedByIM = false;
2108     }
2109
2110     _data->_page->confirmComposition();
2111 }
2112
2113 - (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange
2114 {
2115     [self _executeSavedKeypressCommands];
2116
2117     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
2118     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
2119
2120     LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelectedRange.location, newSelectedRange.length);
2121
2122     // Use pointer to get parameters passed to us by the caller of interpretKeyEvents.
2123     WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters;
2124
2125     if (parameters) {
2126         parameters->eventInterpretationHadSideEffects = true;
2127         parameters->consumedByIM = false;
2128     }
2129     
2130     Vector<CompositionUnderline> underlines;
2131     NSString *text;
2132
2133     if (isAttributedString) {
2134         // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation.
2135         text = [string string];
2136         extractUnderlines(string, underlines);
2137     } else
2138         text = string;
2139
2140     if (_data->_page->editorState().isInPasswordField) {
2141         // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField.
2142         // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard.
2143         ASSERT(!_data->_page->editorState().hasComposition);
2144         [self _notifyInputContextAboutDiscardedComposition];
2145         if ([text length] == 1 && [[text decomposedStringWithCanonicalMapping] characterAtIndex:0] < 0x80) {
2146             _data->_page->insertText(text, replacementRange);
2147         } else
2148             NSBeep();
2149         return;
2150     }
2151
2152     _data->_page->setComposition(text, underlines, newSelectedRange, replacementRange);
2153 }
2154
2155 - (NSRange)markedRange
2156 {
2157     [self _executeSavedKeypressCommands];
2158
2159     EditingRange markedRange;
2160     _data->_page->getMarkedRange(markedRange);
2161
2162     NSRange result = markedRange;
2163     if (result.location == NSNotFound)
2164         LOG(TextInput, "markedRange -> (NSNotFound, %u)", result.length);
2165     else
2166         LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length);
2167
2168     return result;
2169 }
2170
2171 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange
2172 {
2173     [self _executeSavedKeypressCommands];
2174
2175     if (!_data->_page->editorState().isContentEditable) {
2176         LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length);
2177         return nil;
2178     }
2179
2180     if (_data->_page->editorState().isInPasswordField)
2181         return nil;
2182
2183     AttributedString result;
2184     _data->_page->getAttributedSubstringFromRange(nsRange, result);
2185
2186     if (actualRange) {
2187         *actualRange = nsRange;
2188         actualRange->length = [result.string length];
2189     }
2190
2191     LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result.string string]);
2192     return [[result.string retain] autorelease];
2193 }
2194
2195 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
2196 {
2197     [self _executeSavedKeypressCommands];
2198
2199     NSWindow *window = [self window];
2200     
2201 #pragma clang diagnostic push
2202 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2203     if (window)
2204         thePoint = [window convertScreenToBase:thePoint];
2205 #pragma clang diagnostic pop
2206     thePoint = [self convertPoint:thePoint fromView:nil];  // the point is relative to the main frame
2207     
2208     uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint));
2209     if (result == notFound)
2210         result = NSNotFound;
2211     LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result);
2212     return result;
2213 }
2214
2215 - (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
2216
2217     [self _executeSavedKeypressCommands];
2218
2219     // Just to match NSTextView's behavior. Regression tests cannot detect this;
2220     // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
2221     // (type something; try ranges (1, -1) and (2, -1).
2222     if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0))
2223         theRange.length = 0;
2224
2225     if (theRange.location == NSNotFound) {
2226         if (actualRange)
2227             *actualRange = theRange;
2228         LOG(TextInput, "firstRectForCharacterRange:(NSNotFound, %u) -> NSZeroRect", theRange.length);
2229         return NSZeroRect;
2230     }
2231
2232     NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange);
2233     resultRect = [self convertRect:resultRect toView:nil];
2234     resultRect = [self.window convertRectToScreen:resultRect];
2235
2236     if (actualRange) {
2237         // FIXME: Update actualRange to match the range of first rect.
2238         *actualRange = theRange;
2239     }
2240
2241     LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height);
2242     return resultRect;
2243 }
2244
2245 - (BOOL)performKeyEquivalent:(NSEvent *)event
2246 {
2247     if (_data->_ignoresNonWheelEvents)
2248         return NO;
2249
2250     // There's a chance that responding to this event will run a nested event loop, and
2251     // fetching a new event might release the old one. Retaining and then autoreleasing
2252     // the current event prevents that from causing a problem inside WebKit or AppKit code.
2253     [[event retain] autorelease];
2254
2255     // We get Esc key here after processing either Esc or Cmd+period. The former starts as a keyDown, and the latter starts as a key equivalent,
2256     // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:.
2257     // Don't interpret this event again, avoiding re-entrancy and infinite loops.
2258     if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask))
2259         return [super performKeyEquivalent:event];
2260
2261     if (_data->_keyDownEventBeingResent) {
2262         // WebCore has already seen the event, no need for custom processing.
2263         // Note that we can get multiple events for each event being re-sent. For example, for Cmd+'=' AppKit
2264         // first performs the original key equivalent, and if that isn't handled, it dispatches a synthetic Cmd+'+'.
2265         return [super performKeyEquivalent:event];
2266     }
2267
2268     ASSERT(event == [NSApp currentEvent]);
2269
2270     [self _disableComplexTextInputIfNecessary];
2271
2272     // Pass key combos through WebCore if there is a key binding available for
2273     // this event. This lets webpages have a crack at intercepting key-modified keypresses.
2274     // FIXME: Why is the firstResponder check needed?
2275     if (self == [[self window] firstResponder]) {
2276         Vector<KeypressCommand> commands;
2277         BOOL handledByInputMethod = [self _interpretKeyEvent:event savingCommandsTo:commands];
2278         _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands));
2279         return YES;
2280     }
2281     
2282     return [super performKeyEquivalent:event];
2283 }
2284
2285 - (void)keyUp:(NSEvent *)theEvent
2286 {
2287     if (_data->_ignoresNonWheelEvents)
2288         return;
2289
2290     LOG(TextInput, "keyUp:%p %@", theEvent, theEvent);
2291     // We don't interpret the keyUp event, as this breaks key bindings (see <https://bugs.webkit.org/show_bug.cgi?id=130100>).
2292     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>()));
2293 }
2294
2295 - (void)keyDown:(NSEvent *)theEvent
2296 {
2297     if (_data->_ignoresNonWheelEvents)
2298         return;
2299
2300     LOG(TextInput, "keyDown:%p %@%s", theEvent, theEvent, (theEvent == _data->_keyDownEventBeingResent) ? " (re-sent)" : "");
2301
2302     // There's a chance that responding to this event will run a nested event loop, and
2303     // fetching a new event might release the old one. Retaining and then autoreleasing
2304     // the current event prevents that from causing a problem inside WebKit or AppKit code.
2305     [[theEvent retain] autorelease];
2306
2307     if ([self _tryHandlePluginComplexTextInputKeyDown:theEvent]) {
2308         LOG(TextInput, "...handled by plug-in");
2309         return;
2310     }
2311
2312     // We could be receiving a key down from AppKit if we have re-sent an event
2313     // that maps to an action that is currently unavailable (for example a copy when
2314     // there is no range selection).
2315     // If this is the case we should ignore the key down.
2316     if (_data->_keyDownEventBeingResent == theEvent) {
2317         [super keyDown:theEvent];
2318         return;
2319     }
2320
2321     Vector<KeypressCommand> commands;
2322     BOOL handledByInputMethod = [self _interpretKeyEvent:theEvent savingCommandsTo:commands];
2323     if (!commands.isEmpty()) {
2324         // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
2325         // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
2326         // should be handled like normal text input after DOM event dispatch.
2327         handledByInputMethod = NO;
2328     }
2329
2330     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands));
2331 }
2332
2333 - (void)flagsChanged:(NSEvent *)theEvent
2334 {
2335     if (_data->_ignoresNonWheelEvents)
2336         return;
2337
2338     LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent);
2339
2340     // There's a chance that responding to this event will run a nested event loop, and
2341     // fetching a new event might release the old one. Retaining and then autoreleasing
2342     // the current event prevents that from causing a problem inside WebKit or AppKit code.
2343     [[theEvent retain] autorelease];
2344
2345     unsigned short keyCode = [theEvent keyCode];
2346
2347     // Don't make an event from the num lock and function keys
2348     if (!keyCode || keyCode == 10 || keyCode == 63)
2349         return;
2350
2351     _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>()));
2352 }
2353
2354 #endif // USE(ASYNC_NSTEXTINPUTCLIENT)
2355
2356 - (NSArray *)validAttributesForMarkedText
2357 {
2358     static NSArray *validAttributes;
2359     if (!validAttributes) {
2360         validAttributes = [[NSArray alloc] initWithObjects:
2361                            NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName,
2362                            NSMarkedClauseSegmentAttributeName,
2363 #if USE(DICTATION_ALTERNATIVES)
2364                            NSTextAlternativesAttributeName,
2365 #endif
2366 #if USE(INSERTION_UNDO_GROUPING)
2367                            NSTextInsertionUndoableAttributeName,
2368 #endif
2369                            nil];
2370         // NSText also supports the following attributes, but it's
2371         // hard to tell which are really required for text input to
2372         // work well; I have not seen any input method make use of them yet.
2373         //     NSFontAttributeName, NSForegroundColorAttributeName,
2374         //     NSBackgroundColorAttributeName, NSLanguageAttributeName.
2375         CFRetain(validAttributes);
2376     }
2377     LOG(TextInput, "validAttributesForMarkedText -> (...)");
2378     return validAttributes;
2379 }
2380
2381 #if ENABLE(DRAG_SUPPORT)
2382 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
2383 {
2384     NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint];
2385     NSPoint windowMouseLoc = windowImageLoc;
2386    
2387     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
2388     _data->_ignoringMouseDraggedEvents = YES;
2389     
2390     _data->_page->dragEnded(IntPoint(windowMouseLoc), globalPoint(windowMouseLoc, [self window]), operation);
2391 }
2392
2393 - (DragApplicationFlags)applicationFlags:(id <NSDraggingInfo>)draggingInfo
2394 {
2395     uint32_t flags = 0;
2396     if ([NSApp modalWindow])
2397         flags = DragApplicationIsModal;
2398     if ([[self window] attachedSheet])
2399         flags |= DragApplicationHasAttachedSheet;
2400     if ([draggingInfo draggingSource] == self)
2401         flags |= DragApplicationIsSource;
2402     if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
2403         flags |= DragApplicationIsCopyKeyDown;
2404     return static_cast<DragApplicationFlags>(flags);
2405 }
2406
2407 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo
2408 {
2409     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
2410     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
2411     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
2412
2413     _data->_page->resetCurrentDragInformation();
2414     _data->_page->dragEntered(dragData, [[draggingInfo draggingPasteboard] name]);
2415     return NSDragOperationCopy;
2416 }
2417
2418 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo
2419 {
2420     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
2421     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
2422     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
2423     _data->_page->dragUpdated(dragData, [[draggingInfo draggingPasteboard] name]);
2424     
2425     NSInteger numberOfValidItemsForDrop = _data->_page->currentDragNumberOfFilesToBeAccepted();
2426     NSDraggingFormation draggingFormation = NSDraggingFormationNone;
2427     if (_data->_page->currentDragIsOverFileInput() && numberOfValidItemsForDrop > 0)
2428         draggingFormation = NSDraggingFormationList;
2429
2430     if ([draggingInfo numberOfValidItemsForDrop] != numberOfValidItemsForDrop)
2431         [draggingInfo setNumberOfValidItemsForDrop:numberOfValidItemsForDrop];
2432     if ([draggingInfo draggingFormation] != draggingFormation)
2433         [draggingInfo setDraggingFormation:draggingFormation];
2434
2435     return _data->_page->currentDragOperation();
2436 }
2437
2438 - (void)draggingExited:(id <NSDraggingInfo>)draggingInfo
2439 {
2440     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
2441     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
2442     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
2443     _data->_page->dragExited(dragData, [[draggingInfo draggingPasteboard] name]);
2444     _data->_page->resetCurrentDragInformation();
2445 }
2446
2447 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo
2448 {
2449     return YES;
2450 }
2451
2452 // FIXME: This code is more or less copied from Pasteboard::getBestURL.
2453 // It would be nice to be able to share the code somehow.
2454 static bool maybeCreateSandboxExtensionFromPasteboard(NSPasteboard *pasteboard, SandboxExtension::Handle& sandboxExtensionHandle)
2455 {
2456     NSArray *types = [pasteboard types];
2457     if (![types containsObject:NSFilenamesPboardType])
2458         return false;
2459
2460     NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
2461     if ([files count] != 1)
2462         return false;
2463
2464     NSString *file = [files objectAtIndex:0];
2465     BOOL isDirectory;
2466     if (![[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory])
2467         return false;
2468
2469     if (isDirectory)
2470         return false;
2471
2472     SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle);
2473     return true;
2474 }
2475
2476 static void createSandboxExtensionsForFileUpload(NSPasteboard *pasteboard, SandboxExtension::HandleArray& handles)
2477 {
2478     NSArray *types = [pasteboard types];
2479     if (![types containsObject:NSFilenamesPboardType])
2480         return;
2481
2482     NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
2483     handles.allocate([files count]);
2484     for (unsigned i = 0; i < [files count]; i++) {
2485         NSString *file = [files objectAtIndex:i];
2486         if (![[NSFileManager defaultManager] fileExistsAtPath:file])
2487             continue;
2488         SandboxExtension::Handle handle;
2489         SandboxExtension::createHandle(file, SandboxExtension::ReadOnly, handles[i]);
2490     }
2491 }
2492
2493 - (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo
2494 {
2495     IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]);
2496     IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
2497     DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]);
2498
2499     SandboxExtension::Handle sandboxExtensionHandle;
2500     bool createdExtension = maybeCreateSandboxExtensionFromPasteboard([draggingInfo draggingPasteboard], sandboxExtensionHandle);
2501     if (createdExtension)
2502         _data->_page->process().willAcquireUniversalFileReadSandboxExtension();
2503
2504     SandboxExtension::HandleArray sandboxExtensionForUpload;
2505     createSandboxExtensionsForFileUpload([draggingInfo draggingPasteboard], sandboxExtensionForUpload);
2506
2507     _data->_page->performDragOperation(dragData, [[draggingInfo draggingPasteboard] name], sandboxExtensionHandle, sandboxExtensionForUpload);
2508
2509     return YES;
2510 }
2511
2512 // This code is needed to support drag and drop when the drag types cannot be matched.
2513 // This is the case for elements that do not place content
2514 // in the drag pasteboard automatically when the drag start (i.e. dragging a DIV element).
2515 - (NSView *)_hitTest:(NSPoint *)point dragTypes:(NSSet *)types
2516 {
2517     if ([[self superview] mouse:*point inRect:[self frame]])
2518         return self;
2519     return nil;
2520 }
2521 #endif // ENABLE(DRAG_SUPPORT)
2522
2523 - (BOOL)_windowResizeMouseLocationIsInVisibleScrollerThumb:(NSPoint)loc
2524 {
2525     NSPoint localPoint = [self convertPoint:loc fromView:nil];
2526     NSRect visibleThumbRect = NSRect(_data->_page->visibleScrollerThumbRect());
2527     return NSMouseInRect(localPoint, visibleThumbRect, [self isFlipped]);
2528 }
2529
2530 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2531 static void* keyValueObservingContext = &keyValueObservingContext;
2532 #endif
2533
2534 - (void)addWindowObserversForWindow:(NSWindow *)window
2535 {
2536     if (window) {
2537         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:)
2538                                                      name:NSWindowDidBecomeKeyNotification object:nil];
2539         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:)
2540                                                      name:NSWindowDidResignKeyNotification object:nil];
2541         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidMiniaturize:) 
2542                                                      name:NSWindowDidMiniaturizeNotification object:window];
2543         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidDeminiaturize:)
2544                                                      name:NSWindowDidDeminiaturizeNotification object:window];
2545         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidMove:)
2546                                                      name:NSWindowDidMoveNotification object:window];
2547         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResize:) 
2548                                                      name:NSWindowDidResizeNotification object:window];
2549         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidOrderOffScreen:) 
2550                                                      name:@"NSWindowDidOrderOffScreenNotification" object:window];
2551         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidOrderOnScreen:) 
2552                                                      name:@"_NSWindowDidBecomeVisible" object:window];
2553         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeBackingProperties:)
2554                                                      name:NSWindowDidChangeBackingPropertiesNotification object:window];
2555         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeScreen:)
2556                                                      name:NSWindowDidChangeScreenNotification object:window];
2557         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeLayerHosting:)
2558                                                      name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
2559         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeOcclusionState:)
2560                                                      name:NSWindowDidChangeOcclusionStateNotification object:window];
2561         [[NSFontPanel sharedFontPanel] addObserver:self
2562                                         forKeyPath:@"visible"
2563                                            options:NSKeyValueObservingOptionNew
2564                                            context:nil];
2565 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2566         [window addObserver:self forKeyPath:@"contentLayoutRect" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
2567         [window addObserver:self forKeyPath:@"titlebarAppearsTransparent" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
2568 #endif
2569     }
2570 }
2571
2572 - (void)removeWindowObservers
2573 {
2574     NSWindow *window = _data->_targetWindowForMovePreparation ? _data->_targetWindowForMovePreparation : [self window];
2575     if (!window)
2576         return;
2577
2578     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
2579     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
2580     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
2581     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
2582     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMoveNotification object:window];
2583     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:window];
2584     [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NSWindowWillOrderOffScreenNotification" object:window];
2585     [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NSWindowDidOrderOffScreenNotification" object:window];
2586     [[NSNotificationCenter defaultCenter] removeObserver:self name:@"_NSWindowDidBecomeVisible" object:window];
2587     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window];
2588     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidChangeScreenNotification object:window];
2589     [[NSNotificationCenter defaultCenter] removeObserver:self name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
2590     [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidChangeOcclusionStateNotification object:window];
2591     [[NSFontPanel sharedFontPanel] removeObserver:self forKeyPath:@"visible" context:nil];
2592 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2593     [window removeObserver:self forKeyPath:@"contentLayoutRect" context:keyValueObservingContext];
2594     [window removeObserver:self forKeyPath:@"titlebarAppearsTransparent" context:keyValueObservingContext];
2595 #endif
2596 }
2597
2598 - (void)viewWillMoveToWindow:(NSWindow *)window
2599 {
2600     // If we're in the middle of preparing to move to a window, we should only be moved to that window.
2601     ASSERT(!_data->_targetWindowForMovePreparation || (_data->_targetWindowForMovePreparation == window));
2602
2603     NSWindow *currentWindow = [self window];
2604     if (window == currentWindow)
2605         return;
2606
2607     _data->_pageClient->viewWillMoveToAnotherWindow();
2608     
2609     [self removeWindowObservers];
2610     [self addWindowObserversForWindow:window];
2611 }
2612
2613 - (void)viewDidMoveToWindow
2614 {
2615     NSWindow *window = _data->_targetWindowForMovePreparation ? _data->_targetWindowForMovePreparation : self.window;
2616
2617     if (window) {
2618         [self doWindowDidChangeScreen];
2619
2620         ViewState::Flags viewStateChanges = ViewState::WindowIsActive | ViewState::IsVisible;
2621         if ([self isDeferringViewInWindowChanges])
2622             _data->_viewInWindowChangeWasDeferred = YES;
2623         else
2624             viewStateChanges |= ViewState::IsInWindow;
2625         _data->_page->viewStateDidChange(viewStateChanges);
2626
2627         [self _updateWindowAndViewFrames];
2628
2629         // FIXME(135509) This call becomes unnecessary once 135509 is fixed; remove.
2630         _data->_page->layerHostingModeDidChange();
2631
2632         if (!_data->_flagsChangedEventMonitor) {
2633             _data->_flagsChangedEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSFlagsChangedMask handler:^(NSEvent *flagsChangedEvent) {
2634                 [self _postFakeMouseMovedEventForFlagsChangedEvent:flagsChangedEvent];
2635                 return flagsChangedEvent;
2636             }];
2637         }
2638
2639         [self _accessibilityRegisterUIProcessTokens];
2640
2641 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2642         if (_data->_immediateActionGestureRecognizer && ![[self gestureRecognizers] containsObject:_data->_immediateActionGestureRecognizer.get()] && !_data->_ignoresNonWheelEvents)
2643             [self addGestureRecognizer:_data->_immediateActionGestureRecognizer.get()];
2644 #endif
2645     } else {
2646         ViewState::Flags viewStateChanges = ViewState::WindowIsActive | ViewState::IsVisible;
2647         if ([self isDeferringViewInWindowChanges])
2648             _data->_viewInWindowChangeWasDeferred = YES;
2649         else
2650             viewStateChanges |= ViewState::IsInWindow;
2651         _data->_page->viewStateDidChange(viewStateChanges);
2652
2653         [NSEvent removeMonitor:_data->_flagsChangedEventMonitor];
2654         _data->_flagsChangedEventMonitor = nil;
2655
2656         [self _dismissContentRelativeChildWindows];
2657
2658 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2659         if (_data->_immediateActionGestureRecognizer)
2660             [self removeGestureRecognizer:_data->_immediateActionGestureRecognizer.get()];
2661 #endif
2662     }
2663
2664     _data->_page->setIntrinsicDeviceScaleFactor([self _intrinsicDeviceScaleFactor]);
2665 }
2666
2667 - (void)doWindowDidChangeScreen
2668 {
2669     NSWindow *window = _data->_targetWindowForMovePreparation ? _data->_targetWindowForMovePreparation : self.window;
2670     _data->_page->windowScreenDidChange((PlatformDisplayID)[[[[window screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue]);
2671 }
2672
2673 - (void)_windowDidBecomeKey:(NSNotification *)notification
2674 {
2675     NSWindow *keyWindow = [notification object];
2676     if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet]) {
2677         [self _updateSecureInputState];
2678         _data->_page->viewStateDidChange(ViewState::WindowIsActive);
2679     }
2680 }
2681
2682 - (void)_windowDidChangeScreen:(NSNotification *)notification
2683 {
2684     [self doWindowDidChangeScreen];
2685 }
2686
2687 - (void)_windowDidChangeLayerHosting:(NSNotification *)notification
2688 {
2689     _data->_page->layerHostingModeDidChange();
2690 }
2691
2692 - (void)_windowDidResignKey:(NSNotification *)notification
2693 {
2694     NSWindow *formerKeyWindow = [notification object];
2695     if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) {
2696         [self _updateSecureInputState];
2697         _data->_page->viewStateDidChange(ViewState::WindowIsActive);
2698     }
2699 }
2700
2701 - (void)_windowDidMiniaturize:(NSNotification *)notification
2702 {
2703     _data->_page->viewStateDidChange(ViewState::IsVisible);
2704 }
2705
2706 - (void)_windowDidDeminiaturize:(NSNotification *)notification
2707 {
2708     _data->_page->viewStateDidChange(ViewState::IsVisible);
2709 }
2710
2711 - (void)_windowDidMove:(NSNotification *)notification
2712 {
2713     [self _updateWindowAndViewFrames];    
2714 }
2715
2716 - (void)_windowDidResize:(NSNotification *)notification
2717 {
2718     [self _updateWindowAndViewFrames];
2719 }
2720
2721 - (void)_windowDidOrderOffScreen:(NSNotification *)notification
2722 {
2723     _data->_page->viewStateDidChange(ViewState::IsVisible | ViewState::WindowIsActive);
2724 }
2725
2726 - (void)_windowDidOrderOnScreen:(NSNotification *)notification
2727 {
2728     _data->_page->viewStateDidChange(ViewState::IsVisible | ViewState::WindowIsActive);
2729 }
2730
2731 - (void)_windowDidChangeBackingProperties:(NSNotification *)notification
2732 {
2733     CGFloat oldBackingScaleFactor = [[notification.userInfo objectForKey:NSBackingPropertyOldScaleFactorKey] doubleValue];
2734     CGFloat newBackingScaleFactor = [self _intrinsicDeviceScaleFactor]; 
2735     if (oldBackingScaleFactor == newBackingScaleFactor)
2736         return; 
2737
2738     _data->_page->setIntrinsicDeviceScaleFactor(newBackingScaleFactor);
2739 }
2740
2741 - (void)_windowDidChangeOcclusionState:(NSNotification *)notification
2742 {
2743     _data->_page->viewStateDidChange(ViewState::IsVisible);
2744 }
2745
2746 - (void)drawRect:(NSRect)rect
2747 {
2748     LOG(View, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
2749     _data->_page->endPrinting();
2750 }
2751
2752 - (BOOL)isOpaque
2753 {
2754     return _data->_page->drawsBackground();
2755 }
2756
2757 - (BOOL)mouseDownCanMoveWindow
2758 {
2759     // -[NSView mouseDownCanMoveWindow] returns YES when the NSView is transparent,
2760     // but we don't want a drag in the NSView to move the window, even if it's transparent.
2761     return NO;
2762 }
2763
2764 - (void)viewDidHide
2765 {
2766     _data->_page->viewStateDidChange(ViewState::IsVisible);
2767 }
2768
2769 - (void)viewDidUnhide
2770 {
2771     _data->_page->viewStateDidChange(ViewState::IsVisible);
2772 }
2773
2774 - (void)viewDidChangeBackingProperties
2775 {
2776     NSColorSpace *colorSpace = [[self window] colorSpace];
2777     if ([colorSpace isEqualTo:_data->_colorSpace.get()])
2778         return;
2779
2780     _data->_colorSpace = nullptr;
2781     if (DrawingAreaProxy *drawingArea = _data->_page->drawingArea())
2782         drawingArea->colorSpaceDidChange();
2783 }
2784
2785 - (void)_activeSpaceDidChange:(NSNotification *)notification
2786 {
2787     _data->_page->viewStateDidChange(ViewState::IsVisible);
2788 }
2789
2790 - (void)_dictionaryLookupPopoverWillClose:(NSNotification *)notification
2791 {
2792     [self _setTextIndicator:nil fadeOut:NO];
2793 }
2794
2795 - (void)_accessibilityRegisterUIProcessTokens
2796 {
2797     // Initialize remote accessibility when the window connection has been established.
2798     NSData *remoteElementToken = WKAXRemoteTokenForElement(self);
2799     NSData *remoteWindowToken = WKAXRemoteTokenForElement([self window]);
2800     IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
2801     IPC::DataReference windowToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]);
2802     _data->_page->registerUIProcessAccessibilityTokens(elementToken, windowToken);
2803 }
2804
2805 - (void)_updateRemoteAccessibilityRegistration:(BOOL)registerProcess
2806 {
2807     // When the tree is connected/disconnected, the remote accessibility registration
2808     // needs to be updated with the pid of the remote process. If the process is going
2809     // away, that information is not present in WebProcess
2810     pid_t pid = 0;
2811     if (registerProcess)
2812         pid = _data->_page->process().processIdentifier();
2813     else if (!registerProcess) {
2814         pid = WKAXRemoteProcessIdentifier(_data->_remoteAccessibilityChild.get());
2815         _data->_remoteAccessibilityChild = nil;
2816     }
2817     if (pid)
2818         WKAXRegisterRemoteProcess(registerProcess, pid); 
2819 }
2820
2821 - (void)enableAccessibilityIfNecessary
2822 {
2823     if (WebCore::AXObjectCache::accessibilityEnabled())
2824         return;
2825
2826     // After enabling accessibility update the window frame on the web process so that the
2827     // correct accessibility position is transmitted (when AX is off, that position is not calculated).
2828     WebCore::AXObjectCache::enableAccessibility();
2829     [self _updateWindowAndViewFrames];
2830 }
2831
2832 - (id)accessibilityFocusedUIElement
2833 {
2834     [self enableAccessibilityIfNecessary];
2835     return _data->_remoteAccessibilityChild.get();
2836 }
2837
2838 - (BOOL)accessibilityIsIgnored
2839 {
2840     return NO;
2841 }
2842
2843 - (id)accessibilityHitTest:(NSPoint)point
2844 {
2845     [self enableAccessibilityIfNecessary];
2846     return _data->_remoteAccessibilityChild.get();
2847 }
2848
2849 - (id)accessibilityAttributeValue:(NSString*)attribute
2850 {
2851     [self enableAccessibilityIfNecessary];
2852
2853     if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
2854
2855         id child = nil;
2856         if (_data->_remoteAccessibilityChild)
2857             child = _data->_remoteAccessibilityChild.get();
2858         
2859         if (!child)
2860             return nil;
2861         return [NSArray arrayWithObject:child];
2862     }
2863     if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
2864         return NSAccessibilityGroupRole;
2865     if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
2866         return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
2867     if ([attribute isEqualToString:NSAccessibilityParentAttribute])
2868         return NSAccessibilityUnignoredAncestor([self superview]);
2869     if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
2870         return [NSNumber numberWithBool:YES];
2871     
2872     return [super accessibilityAttributeValue:attribute];
2873 }
2874
2875 - (NSView *)hitTest:(NSPoint)point
2876 {
2877     NSView *hitView = [super hitTest:point];
2878     if (hitView && _data && hitView == _data->_layerHostingView)
2879         hitView = self;
2880
2881     return hitView;
2882 }
2883
2884 - (void)_postFakeMouseMovedEventForFlagsChangedEvent:(NSEvent *)flagsChangedEvent
2885 {
2886     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved location:[[flagsChangedEvent window] mouseLocationOutsideOfEventStream]
2887         modifierFlags:[flagsChangedEvent modifierFlags] timestamp:[flagsChangedEvent timestamp] windowNumber:[flagsChangedEvent windowNumber]
2888         context:[flagsChangedEvent context] eventNumber:0 clickCount:0 pressure:0];
2889     NativeWebMouseEvent webEvent(fakeEvent, self);
2890     _data->_page->handleMouseEvent(webEvent);
2891 }
2892
2893 - (NSInteger)conversationIdentifier
2894 {
2895     return (NSInteger)self;
2896 }
2897
2898 - (float)_intrinsicDeviceScaleFactor
2899 {
2900     if (_data->_overrideDeviceScaleFactor)
2901         return _data->_overrideDeviceScaleFactor;
2902     if (_data->_targetWindowForMovePreparation)
2903         return [_data->_targetWindowForMovePreparation backingScaleFactor];
2904     if (NSWindow *window = [self window])
2905         return [window backingScaleFactor];
2906     return [[NSScreen mainScreen] backingScaleFactor];
2907 }
2908
2909 - (void)_setDrawingAreaSize:(NSSize)size
2910 {
2911     if (!_data->_page->drawingArea())
2912         return;
2913     
2914     _data->_page->drawingArea()->setSize(IntSize(size), IntSize(0, 0), IntSize(_data->_resizeScrollOffset));
2915     _data->_resizeScrollOffset = NSZeroSize;
2916 }
2917
2918 - (void)quickLookWithEvent:(NSEvent *)event
2919 {
2920     if (_data->_ignoresNonWheelEvents)
2921         return;
2922
2923 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2924     if (_data->_immediateActionGestureRecognizer) {
2925         [super quickLookWithEvent:event];
2926         return;
2927     }
2928 #endif
2929
2930     NSPoint locationInViewCoordinates = [self convertPoint:[event locationInWindow] fromView:nil];
2931     _data->_page->performDictionaryLookupAtLocation(FloatPoint(locationInViewCoordinates.x, locationInViewCoordinates.y));
2932 }
2933
2934 - (std::unique_ptr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy
2935 {
2936     if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"WebKit2UseRemoteLayerTreeDrawingArea"] boolValue])
2937         return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(*_data->_page);
2938
2939     return std::make_unique<TiledCoreAnimationDrawingAreaProxy>(*_data->_page);
2940 }
2941
2942 - (BOOL)_isFocused
2943 {
2944     if (_data->_inBecomeFirstResponder)
2945         return YES;
2946     if (_data->_inResignFirstResponder)
2947         return NO;
2948     return [[self window] firstResponder] == self;
2949 }
2950
2951 - (WebKit::ColorSpaceData)_colorSpace
2952 {
2953     if (!_data->_colorSpace) {
2954         if (_data->_targetWindowForMovePreparation)
2955             _data->_colorSpace = [_data->_targetWindowForMovePreparation colorSpace];
2956         else if (NSWindow *window = [self window])
2957             _data->_colorSpace = [window colorSpace];
2958         else
2959             _data->_colorSpace = [[NSScreen mainScreen] colorSpace];
2960     }
2961         
2962     ColorSpaceData colorSpaceData;
2963     colorSpaceData.cgColorSpace = [_data->_colorSpace CGColorSpace];
2964
2965     return colorSpaceData;    
2966 }
2967
2968 - (void)_processDidExit
2969 {
2970     [self _notifyInputContextAboutDiscardedComposition];
2971
2972     if (_data->_layerHostingView)
2973         [self _setAcceleratedCompositingModeRootLayer:nil];
2974
2975     [self _updateRemoteAccessibilityRegistration:NO];
2976
2977     _data->_gestureController = nullptr;
2978 }
2979
2980 - (void)_pageClosed
2981 {
2982     [self _updateRemoteAccessibilityRegistration:NO];
2983 }
2984
2985 - (void)_didRelaunchProcess
2986 {
2987     [self _accessibilityRegisterUIProcessTokens];
2988 }
2989
2990 - (void)_preferencesDidChange
2991 {
2992     BOOL needsViewFrameInWindowCoordinates = _data->_page->preferences().pluginsEnabled();
2993
2994     if (!!needsViewFrameInWindowCoordinates == !!_data->_needsViewFrameInWindowCoordinates)
2995         return;
2996
2997     _data->_needsViewFrameInWindowCoordinates = needsViewFrameInWindowCoordinates;
2998     if ([self window])
2999         [self _updateWindowAndViewFrames];
3000 }
3001
3002 - (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState
3003 {
3004     ValidationVector items = _data->_validationMap.take(commandName);
3005     size_t size = items.size();
3006     for (size_t i = 0; i < size; ++i) {
3007         ValidationItem item = items[i].get();
3008         [menuItem(item) setState:newState];
3009         [menuItem(item) setEnabled:isEnabled];
3010         [toolbarItem(item) setEnabled:isEnabled];
3011         // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled.
3012     }
3013 }
3014
3015 - (BOOL)_tryPostProcessPluginComplexTextInputKeyDown:(NSEvent *)event
3016 {
3017     if (!_data->_pluginComplexTextInputIdentifier || _data->_pluginComplexTextInputState == PluginComplexTextInputDisabled)
3018         return NO;
3019
3020     // In the legacy text input model, the event has already been sent to the input method.
3021     if (_data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
3022         return NO;
3023
3024     return [self _handlePluginComplexTextInputKeyDown:event];
3025 }
3026
3027 - (void)_doneWithKeyEvent:(NSEvent *)event eventWasHandled:(BOOL)eventWasHandled
3028 {
3029     if ([event type] != NSKeyDown)
3030         return;
3031
3032     if ([self _tryPostProcessPluginComplexTextInputKeyDown:event])
3033         return;
3034     
3035     if (eventWasHandled) {
3036         [NSCursor setHiddenUntilMouseMoves:YES];
3037         return;
3038     }
3039
3040     // resending the event may destroy this WKView
3041     RetainPtr<WKView> protector(self);
3042
3043     ASSERT(!_data->_keyDownEventBeingResent);
3044     _data->_keyDownEventBeingResent = event;
3045     [NSApp _setCurrentEvent:event];
3046     [NSApp sendEvent:event];
3047
3048     _data->_keyDownEventBeingResent = nullptr;
3049 }
3050
3051 - (NSRect)_convertToDeviceSpace:(NSRect)rect
3052 {
3053     return toDeviceSpace(rect, [self window]);
3054 }
3055
3056 - (NSRect)_convertToUserSpace:(NSRect)rect
3057 {
3058     return toUserSpace(rect, [self window]);
3059 }
3060
3061 // Any non-zero value will do, but using something recognizable might help us debug some day.
3062 #define TRACKING_RECT_TAG 0xBADFACE
3063
3064 - (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside
3065 {
3066     ASSERT(_data->_trackingRectOwner == nil);
3067     _data->_trackingRectOwner = owner;
3068     _data->_trackingRectUserData = data;
3069     return TRACKING_RECT_TAG;
3070 }
3071
3072 - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag
3073 {
3074     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
3075     ASSERT(_data->_trackingRectOwner == nil);
3076     _data->_trackingRectOwner = owner;
3077     _data->_trackingRectUserData = data;
3078     return TRACKING_RECT_TAG;
3079 }
3080
3081 - (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count
3082 {
3083     ASSERT(count == 1);
3084     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
3085     ASSERT(_data->_trackingRectOwner == nil);
3086     _data->_trackingRectOwner = owner;
3087     _data->_trackingRectUserData = userDataList[0];
3088     trackingNums[0] = TRACKING_RECT_TAG;
3089 }
3090
3091 - (void)removeTrackingRect:(NSTrackingRectTag)tag
3092 {
3093     if (!_data)
3094         return;
3095
3096     if (tag == 0)
3097         return;
3098     
3099     if (tag == TRACKING_RECT_TAG) {
3100         _data->_trackingRectOwner = nil;
3101         return;
3102     }
3103     
3104     if (tag == _data->_lastToolTipTag) {
3105         [super removeTrackingRect:tag];
3106         _data->_lastToolTipTag = 0;
3107         return;
3108     }
3109
3110     // If any other tracking rect is being removed, we don't know how it was created
3111     // and it's possible there's a leak involved (see 3500217)
3112     ASSERT_NOT_REACHED();
3113 }
3114
3115 - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count
3116 {
3117     int i;
3118     for (i = 0; i < count; ++i) {
3119         int tag = tags[i];
3120         if (tag == 0)
3121             continue;
3122         ASSERT(tag == TRACKING_RECT_TAG);
3123         if (_data != nil) {
3124             _data->_trackingRectOwner = nil;
3125         }
3126     }
3127 }
3128
3129 - (void)_sendToolTipMouseExited
3130 {
3131     // Nothing matters except window, trackingNumber, and userData.
3132     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
3133         location:NSMakePoint(0, 0)
3134         modifierFlags:0
3135         timestamp:0
3136         windowNumber:[[self window] windowNumber]
3137         context:NULL
3138         eventNumber:0
3139         trackingNumber:TRACKING_RECT_TAG
3140         userData:_data->_trackingRectUserData];
3141     [_data->_trackingRectOwner mouseExited:fakeEvent];
3142 }
3143
3144 - (void)_sendToolTipMouseEntered
3145 {
3146     // Nothing matters except window, trackingNumber, and userData.
3147     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
3148         location:NSMakePoint(0, 0)
3149         modifierFlags:0
3150         timestamp:0
3151         windowNumber:[[self window] windowNumber]
3152         context:NULL
3153         eventNumber:0
3154         trackingNumber:TRACKING_RECT_TAG
3155         userData:_data->_trackingRectUserData];
3156     [_data->_trackingRectOwner mouseEntered:fakeEvent];
3157 }
3158
3159 - (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data
3160 {
3161     return nsStringFromWebCoreString(_data->_page->toolTip());
3162 }
3163
3164 - (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip
3165 {
3166     if (oldToolTip)
3167         [self _sendToolTipMouseExited];
3168
3169     if (newToolTip && [newToolTip length] > 0) {
3170         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
3171         [self removeAllToolTips];
3172         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
3173         _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL];
3174         [self _sendToolTipMouseEntered];
3175     }
3176 }
3177
3178 - (void)_setTextIndicator:(PassRefPtr<TextIndicator>)textIndicator fadeOut:(BOOL)fadeOut
3179 {
3180     if (!textIndicator) {
3181         _data->_textIndicatorWindow = nullptr;
3182         return;
3183     }
3184
3185     if (!_data->_textIndicatorWindow)
3186         _data->_textIndicatorWindow = std::make_unique<TextIndicatorWindow>(self);
3187
3188     NSRect textBoundingRectInScreenCoordinates = [self.window convertRectToScreen:[self convertRect:textIndicator->textBoundingRectInRootViewCoordinates() toView:nil]];
3189     _data->_textIndicatorWindow->setTextIndicator(textIndicator, NSRectToCGRect(textBoundingRectInScreenCoordinates), fadeOut);
3190 }
3191
3192 - (void)_setTextIndicatorAnimationProgress:(float)progress
3193 {
3194     if (_data->_textIndicatorWindow)
3195         _data->_textIndicatorWindow->setAnimationProgress(progress);
3196 }
3197
3198 - (CALayer *)_rootLayer
3199 {
3200     return [_data->_layerHostingView layer];
3201 }
3202
3203 - (void)_setAcceleratedCompositingModeRootLayer:(CALayer *)rootLayer
3204 {
3205     [rootLayer web_disableAllActions];
3206
3207     _data->_rootLayer = rootLayer;
3208
3209 #if WK_API_ENABLED
3210     if (_data->_thumbnailView) {
3211         [self _updateThumbnailViewLayer];
3212         return;
3213     }
3214 #endif
3215
3216     [CATransaction begin];
3217     [CATransaction setDisableActions:YES];
3218
3219     if (rootLayer) {
3220         if (!_data->_layerHostingView) {
3221             // Create an NSView that will host our layer tree.
3222             _data->_layerHostingView = adoptNS([[WKFlippedView alloc] initWithFrame:[self bounds]]);
3223             [_data->_layerHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
3224
3225
3226             [self addSubview:_data->_layerHostingView.get() positioned:NSWindowBelow relativeTo:nil];
3227
3228             // Create a root layer that will back the NSView.
3229             RetainPtr<CALayer> layer = adoptNS([[CALayer alloc] init]);
3230             [layer setDelegate:[WebActionDisablingCALayerDelegate shared]];
3231 #ifndef NDEBUG
3232             [layer setName:@"Hosting root layer"];
3233 #endif
3234
3235             [_data->_layerHostingView setLayer:layer.get()];
3236             [_data->_layerHostingView setWantsLayer:YES];
3237         }
3238
3239         [_data->_layerHostingView layer].sublayers = [NSArray arrayWithObject:rootLayer];
3240     } else {
3241         if (_data->_layerHostingView) {
3242             [_data->_layerHostingView removeFromSuperview];
3243             [_data->_layerHostingView setLayer:nil];
3244             [_data->_layerHostingView setWantsLayer:NO];
3245
3246             _data->_layerHostingView = nullptr;
3247         }
3248     }
3249
3250     [CATransaction commit];
3251 }
3252
3253 - (CALayer *)_acceleratedCompositingModeRootLayer
3254 {
3255     return _data->_rootLayer.get();
3256 }
3257
3258 - (PassRefPtr<ViewSnapshot>)_takeViewSnapshot
3259 {
3260     NSWindow *window = self.window;
3261
3262     CGSWindowID windowID = (CGSWindowID)[window windowNumber];
3263     if (!windowID || ![window isVisible])
3264         return nullptr;
3265
3266     RetainPtr<CGImageRef> windowSnapshotImage = adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque));
3267
3268     // Work around <rdar://problem/17084993>; re-request the snapshot at kCGWindowImageNominalResolution if it was captured at the wrong scale.
3269     CGFloat desiredSnapshotWidth = window.frame.size.width * window.screen.backingScaleFactor;
3270     if (CGImageGetWidth(windowSnapshotImage.get()) != desiredSnapshotWidth)
3271         windowSnapshotImage = adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque | kCGWindowImageNominalResolution));
3272
3273     [self _ensureGestureController];
3274
3275     NSRect windowCaptureRect;
3276     FloatRect boundsForCustomSwipeViews = _data->_gestureController->windowRelativeBoundsForCustomSwipeViews();
3277     if (!boundsForCustomSwipeViews.isEmpty())
3278         windowCaptureRect = boundsForCustomSwipeViews;
3279     else {
3280         NSRect unobscuredBounds = self.bounds;
3281         float topContentInset = _data->_page->topContentInset();
3282         unobscuredBounds.origin.y += topContentInset;
3283         unobscuredBounds.size.height -= topContentInset;
3284         windowCaptureRect = [self convertRect:unobscuredBounds toView:nil];
3285     }
3286
3287     NSRect windowCaptureScreenRect = [window convertRectToScreen:windowCaptureRect];
3288     CGRect windowScreenRect;
3289     CGSGetScreenRectForWindow(CGSMainConnectionID(), (CGSWindowID)[window windowNumber], &windowScreenRect);
3290
3291     NSRect croppedImageRect = windowCaptureRect;
3292     croppedImageRect.origin.y = windowScreenRect.size.height - windowCaptureScreenRect.size.height - NSMinY(windowCaptureRect);
3293
3294     auto croppedSnapshotImage = adoptCF(CGImageCreateWithImageInRect(windowSnapshotImage.get(), NSRectToCGRect([window convertRectToBacking:croppedImageRect])));
3295
3296     auto surface = IOSurface::createFromImage(croppedSnapshotImage.get());
3297     if (!surface)
3298         return nullptr;
3299     surface->setIsVolatile(true);
3300
3301     return ViewSnapshot::create(WTF::move(surface));
3302 }
3303
3304 - (void)_wheelEventWasNotHandledByWebCore:(NSEvent *)event
3305 {
3306     if (_data->_gestureController)
3307         _data->_gestureController->wheelEventWasNotHandledByWebCore(event);
3308 }
3309
3310 - (void)_setAccessibilityWebProcessToken:(NSData *)data
3311 {
3312     _data->_remoteAccessibilityChild = WKAXRemoteElementForToken(data);
3313     [self _updateRemoteAccessibilityRegistration:YES];
3314 }
3315
3316 - (void)_pluginFocusOrWindowFocusChanged:(BOOL)pluginHasFocusAndWindowHasFocus pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier
3317 {
3318     BOOL inputSourceChanged = _data->_pluginComplexTextInputIdentifier;
3319
3320     if (pluginHasFocusAndWindowHasFocus) {
3321         // Check if we're already allowing text input for this plug-in.
3322         if (pluginComplexTextInputIdentifier == _data->_pluginComplexTextInputIdentifier)
3323             return;
3324
3325         _data->_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier;
3326
3327     } else {
3328         // Check if we got a request to unfocus a plug-in that isn't focused.
3329         if (pluginComplexTextInputIdentifier != _data->_pluginComplexTextInputIdentifier)
3330             return;
3331
3332         _data->_pluginComplexTextInputIdentifier = 0;
3333     }
3334
3335     if (inputSourceChanged) {
3336         // The input source changed, go ahead and discard any entered text.
3337         [[WKTextInputWindowController sharedTextInputWindowController] unmarkText];
3338     }
3339
3340     // This will force the current input context to be updated to its correct value.
3341     [NSApp updateWindows];
3342 }
3343
3344 - (void)_setPluginComplexTextInputState:(PluginComplexTextInputState)pluginComplexTextInputState pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier
3345 {
3346     if (pluginComplexTextInputIdentifier != _data->_pluginComplexTextInputIdentifier) {
3347         // We're asked to update the state for a plug-in that doesn't have focus.
3348         return;
3349     }
3350
3351     [self _setPluginComplexTextInputState:pluginComplexTextInputState];
3352 }
3353
3354 - (void)_setDragImage:(NSImage *)image at:(NSPoint)clientPoint linkDrag:(BOOL)linkDrag
3355 {
3356     IntSize size([image size]);
3357     size.scale(1.0 / _data->_page->deviceScaleFactor());
3358     [image setSize:size];
3359     
3360     // The call below could release this WKView.
3361     RetainPtr<WKView> protector(self);
3362     
3363 #pragma clang diagnostic push
3364 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3365     [self dragImage:image
3366                  at:clientPoint
3367              offset:NSZeroSize
3368               event:(linkDrag) ? [NSApp currentEvent] :_data->_mouseDownEvent
3369          pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
3370              source:self
3371           slideBack:YES];
3372 #pragma clang diagnostic pop
3373 }
3374
3375 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
3376 {
3377     NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
3378     return hasCaseInsensitiveSuffix(filename, extensionAsSuffix) || (stringIsCaseInsensitiveEqualToString(extension, @"jpeg")
3379                                                                      && hasCaseInsensitiveSuffix(filename, @".jpg"));
3380 }
3381
3382 - (void)_setPromisedData:(WebCore::Image *)image withFileName:(NSString *)filename withExtension:(NSString *)extension withTitle:(NSString *)title withURL:(NSString *)url withVisibleURL:(NSString *)visibleUrl withArchive:(WebCore::SharedBuffer*) archiveBuffer forPasteboard:(NSString *)pasteboardName
3383
3384 {
3385     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
3386     RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
3387     
3388     [types addObjectsFromArray:archiveBuffer ? PasteboardTypes::forImagesWithArchive() : PasteboardTypes::forImages()];
3389     [pasteboard declareTypes:types.get() owner:self];
3390     if (!matchesExtensionOrEquivalent(filename, extension))
3391         filename = [[filename stringByAppendingString:@"."] stringByAppendingString:extension];
3392
3393     [pasteboard setString:visibleUrl forType:NSStringPboardType];
3394     [pasteboard setString:visibleUrl forType:PasteboardTypes::WebURLPboardType];
3395     [pasteboard setString:title forType:PasteboardTypes::WebURLNamePboardType];
3396     [pasteboard setPropertyList:[NSArray arrayWithObjects:[NSArray arrayWithObject:visibleUrl], [NSArray arrayWithObject:title], nil] forType:PasteboardTypes::WebURLsWithTitlesPboardType];
3397     [pasteboard setPropertyList:[NSArray arrayWithObject:extension] forType:NSFilesPromisePboardType];
3398
3399     if (archiveBuffer)
3400         [pasteboard setData:archiveBuffer->createNSData().get() forType:PasteboardTypes::WebArchivePboardType];
3401
3402     _data->_promisedImage = image;
3403     _data->_promisedFilename = filename;
3404     _data->_promisedURL = url;
3405 }
3406
3407 - (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard
3408 {
3409     _data->_promisedImage = 0;
3410     _data->_promisedFilename = "";
3411     _data->_promisedURL = "";
3412 }
3413
3414 - (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type
3415 {
3416     // FIXME: need to support NSRTFDPboardType
3417
3418     if ([type isEqual:NSTIFFPboardType] && _data->_promisedImage) {
3419         [pasteboard setData:(NSData *)_data->_promisedImage->getTIFFRepresentation() forType:NSTIFFPboardType];
3420         _data->_promisedImage = 0;
3421     }
3422 }
3423
3424 static BOOL fileExists(NSString *path)
3425 {
3426     struct stat statBuffer;
3427     return !lstat([path fileSystemRepresentation], &statBuffer);
3428 }
3429
3430 static NSString *pathWithUniqueFilenameForPath(NSString *path)
3431 {
3432     // "Fix" the filename of the path.
3433     NSString *filename = filenameByFixingIllegalCharacters([path lastPathComponent]);
3434     path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename];
3435     
3436     if (fileExists(path)) {
3437         // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename.
3438         NSString *extensions = nil;
3439         NSString *pathWithoutExtensions;
3440         NSString *lastPathComponent = [path lastPathComponent];
3441         NSRange periodRange = [lastPathComponent rangeOfString:@"."];
3442         
3443         if (periodRange.location == NSNotFound) {
3444             pathWithoutExtensions = path;
3445         } else {
3446             extensions = [lastPathComponent substringFromIndex:periodRange.location + 1];
3447             lastPathComponent = [lastPathComponent substringToIndex:periodRange.location];
3448             pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent];
3449         }
3450         
3451         for (unsigned i = 1; ; i++) {
3452             NSString *pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i];
3453             path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber;
3454             if (!fileExists(path))
3455                 break;
3456         }
3457     }
3458     
3459     return path;
3460 }
3461
3462 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
3463 {
3464     RetainPtr<NSFileWrapper> wrapper;
3465     RetainPtr<NSData> data;
3466     
3467     if (_data->_promisedImage) {
3468         data = _data->_promisedImage->data()->createNSData();
3469         wrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data.get()]);
3470         [wrapper setPreferredFilename:_data->_promisedFilename];
3471     }
3472     
3473     if (!wrapper) {
3474         LOG_ERROR("Failed to create image file.");
3475         return nil;
3476     }
3477     
3478     // FIXME: Report an error if we fail to create a file.
3479     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3480     path = pathWithUniqueFilenameForPath(path);
3481     if (![wrapper writeToURL:[NSURL fileURLWithPath:path] options:NSFileWrapperWritingWithNameUpdating originalContentsURL:nil error:nullptr])
3482         LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToURL:options:originalContentsURL:error:]");
3483
3484     if (!_data->_promisedURL.isEmpty())
3485         WebCore::setMetadataURL(_data->_promisedURL, "", String(path));
3486     
3487     return [NSArray arrayWithObject:[path lastPathComponent]];
3488 }
3489
3490 - (void)_updateSecureInputState
3491 {
3492     if (![[self window] isKeyWindow] || ![self _isFocused]) {
3493         if (_data->_inSecureInputState) {
3494             DisableSecureEventInput();
3495             _data->_inSecureInputState = NO;
3496         }
3497         return;
3498     }
3499     // WKView has a single input context for all editable areas (except for plug-ins).
3500     NSTextInputContext *context = [super inputContext];
3501     bool isInPasswordField = _data->_page->editorState().isInPasswordField;
3502
3503     if (isInPasswordField) {
3504         if (!_data->_inSecureInputState)
3505             EnableSecureEventInput();
3506         static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1];
3507         LOG(TextInput, "-> setAllowedInputSourceLocales:romanInputSources");
3508         [context setAllowedInputSourceLocales:romanInputSources];
3509     } else {
3510         if (_data->_inSecureInputState)
3511             DisableSecureEventInput();
3512         LOG(TextInput, "-> setAllowedInputSourceLocales:nil");
3513         [context setAllowedInputSourceLocales:nil];
3514     }
3515     _data->_inSecureInputState = isInPasswordField;
3516 }
3517
3518 - (void)_resetSecureInputState
3519 {
3520     if (_data->_inSecureInputState) {
3521         DisableSecureEventInput();
3522         _data->_inSecureInputState = NO;
3523     }
3524 }
3525
3526 - (void)_notifyInputContextAboutDiscardedComposition
3527 {
3528     // <rdar://problem/9359055>: -discardMarkedText can only be called for active contexts.
3529     // FIXME: We fail to ever notify the input context if something (e.g. a navigation) happens while the window is not key.
3530     // This is not a problem when the window is key, because we discard marked text on resigning first responder.
3531     if (![[self window] isKeyWindow] || self != [[self window] firstResponder])
3532         return;
3533
3534     LOG(TextInput, "-> discardMarkedText");
3535     [[super inputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to.
3536 }
3537
3538 - (NSWindow *)_targetWindowForMovePreparation
3539 {
3540     return _data->_targetWindowForMovePreparation;
3541 }
3542
3543 #if ENABLE(FULLSCREEN_API)
3544 - (BOOL)_hasFullScreenWindowController
3545 {
3546     return (bool)_data->_fullScreenWindowController;
3547 }
3548
3549 - (WKFullScreenWindowController *)_fullScreenWindowController
3550 {
3551     if (!_data->_fullScreenWindowController)
3552         _data->_fullScreenWindowController = adoptNS([[WKFullScreenWindowController alloc] initWithWindow:[self createFullScreenWindow] webView:self]);
3553
3554     return _data->_fullScreenWindowController.get();
3555 }
3556
3557 - (void)_closeFullScreenWindowController
3558 {
3559     if (!_data->_fullScreenWindowController)
3560         return;
3561
3562     [_data->_fullScreenWindowController close];
3563     _data->_fullScreenWindowController = nullptr;
3564 }
3565 #endif
3566
3567 - (bool)_executeSavedCommandBySelector:(SEL)selector
3568 {
3569     LOG(TextInput, "Executing previously saved command %s", sel_getName(selector));
3570     // The sink does two things: 1) Tells us if the responder went unhandled, and
3571     // 2) prevents any NSBeep; we don't ever want to beep here.
3572     RetainPtr<WKResponderChainSink> sink = adoptNS([[WKResponderChainSink alloc] initWithResponderChain:self]);
3573     [super doCommandBySelector:selector];
3574     [sink detach];
3575     return ![sink didReceiveUnhandledCommand];
3576 }
3577
3578 - (void)_setIntrinsicContentSize:(NSSize)intrinsicContentSize
3579 {
3580     // If the intrinsic content size is less than the minimum layout width, the content flowed to fit,
3581     // so we can report that that dimension is flexible. If not, we need to report our intrinsic width
3582     // so that autolayout will know to provide space for us.
3583
3584     NSSize intrinsicContentSizeAcknowledgingFlexibleWidth = intrinsicContentSize;
3585     if (intrinsicContentSize.width < _data->_page->minimumLayoutSize().width())
3586         intrinsicContentSizeAcknowledgingFlexibleWidth.width = NSViewNoInstrinsicMetric;
3587
3588     _data->_intrinsicContentSize = intrinsicContentSizeAcknowledgingFlexibleWidth;
3589     [self invalidateIntrinsicContentSize];
3590 }
3591
3592 - (void)_cacheWindowBottomCornerRect
3593 {
3594     // FIXME: We should remove this code when <rdar://problem/9362085> is resolved.
3595     NSWindow *window = [self window];
3596     if (!window)
3597         return;
3598
3599     _data->_windowBottomCornerIntersectionRect = [window _intersectBottomCornersWithRect:[self convertRect:[self visibleRect] toView:nil]];
3600     if (!NSIsEmptyRect(_data->_windowBottomCornerIntersectionRect))
3601         [self setNeedsDisplayInRect:[self convertRect:_data->_windowBottomCornerIntersectionRect fromView:nil]];
3602 }
3603
3604 - (NSInteger)spellCheckerDocumentTag
3605 {
3606     if (!_data->_hasSpellCheckerDocumentTag) {
3607         _data->_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
3608         _data->_hasSpellCheckerDocumentTag = YES;
3609     }
3610     return _data->_spellCheckerDocumentTag;
3611 }
3612
3613 - (void)handleAcceptedAlternativeText:(NSString*)text
3614 {
3615     _data->_page->handleAlternativeTextUIResult(text);
3616 }
3617
3618 - (void)_setSuppressVisibilityUpdates:(BOOL)suppressVisibilityUpdates
3619 {
3620     _data->_page->setSuppressVisibilityUpdates(suppressVisibilityUpdates);
3621 }
3622
3623 - (BOOL)_suppressVisibilityUpdates
3624 {
3625     return _data->_page->suppressVisibilityUpdates();
3626 }
3627
3628 - (NSTrackingArea *)_primaryTrackingArea
3629 {
3630     return _data->_primaryTrackingArea.get();
3631 }
3632
3633 - (void)_setPrimaryTrackingArea:(NSTrackingArea *)trackingArea
3634 {
3635     [self removeTrackingArea:_data->_primaryTrackingArea.get()];
3636     _data->_primaryTrackingArea = trackingArea;
3637     [self addTrackingArea:trackingArea];
3638 }
3639
3640 - (instancetype)initWithFrame:(NSRect)frame processPool:(WebProcessPool&)processPool configuration:(WebPageConfiguration)webPageConfiguration webView:(WKWebView *)webView
3641 {
3642     self = [super initWithFrame:frame];
3643     if (!self)
3644         return nil;
3645
3646     [NSApp registerServicesMenuSendTypes:PasteboardTypes::forSelection() returnTypes:PasteboardTypes::forEditing()];
3647
3648     InitializeWebKit2();
3649
3650     // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
3651     NSTrackingAreaOptions options = NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingInVisibleRect;
3652     if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy)
3653         options |= NSTrackingActiveAlways;
3654     else
3655         options |= NSTrackingActiveInKeyWindow;
3656
3657     _data = [[WKViewData alloc] init];
3658     _data->_primaryTrackingArea = adoptNS([[NSTrackingArea alloc] initWithRect:frame options:options owner:self userInfo:nil]);
3659     [self addTrackingArea:_data->_primaryTrackingArea.get()];
3660
3661     _data->_pageClient = std::make_unique<PageClientImpl>(self, webView);
3662     _data->_page = processPool.createWebPage(*_data->_pageClient, WTF::move(webPageConfiguration));
3663     _data->_page->setAddsVisitedLinks(processPool.historyClient().addsVisitedLinks());
3664
3665     _data->_page->setIntrinsicDeviceScaleFactor([self _intrinsicDeviceScaleFactor]);
3666     _data->_page->initializeWebPage();
3667
3668     _data->_mouseDownEvent = nil;
3669     _data->_ignoringMouseDraggedEvents = NO;
3670     _data->_clipsToVisibleRect = NO;
3671     _data->_useContentPreparationRectForVisibleRect = NO;
3672     _data->_windowOcclusionDetectionEnabled = YES;
3673
3674     _data->_intrinsicContentSize = NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric);
3675
3676     _data->_needsViewFrameInWindowCoordinates = _data->_page->preferences().pluginsEnabled();
3677     
3678     [self _registerDraggedTypes];
3679
3680     self.wantsLayer = YES;
3681
3682     // Explicitly set the layer contents placement so AppKit will make sure that our layer has masksToBounds set to YES.
3683     self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
3684
3685     WebProcessPool::statistics().wkViewCount++;
3686
3687     NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
3688     [workspaceNotificationCenter addObserver:self selector:@selector(_activeSpaceDidChange:) name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
3689
3690     if (canLoadLUNotificationPopoverWillClose())
3691         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_dictionaryLookupPopoverWillClose:) name:getLUNotificationPopoverWillClose() object:nil];
3692
3693 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
3694     if ([self respondsToSelector:@selector(_setActionMenu:)]) {
3695         RetainPtr<NSMenu> menu = adoptNS([[NSMenu alloc] init]);
3696         self._actionMenu = menu.get();
3697         _data->_actionMenuController = adoptNS([[WKActionMenuController alloc] initWithPage:*_data->_page view:self]);
3698         self._actionMenu.delegate = _data->_actionMenuController.get();
3699         self._actionMenu.autoenablesItems = NO;
3700     }
3701
3702     // FIXME: We should not permanently disable this for iBooks. rdar://problem/19585689
3703     Class gestureClass = NSClassFromString(@"NSImmediateActionGestureRecognizer");
3704     if (gestureClass && !applicationIsIBooks()) {
3705         _data->_immediateActionGestureRecognizer = adoptNS([(NSImmediateActionGestureRecognizer *)[gestureClass alloc] initWithTarget:nil action:NULL]);
3706         _data->_immediateActionController = adoptNS([[WKImmediateActionController alloc] initWithPage:*_data->_page view:self recognizer:_data->_immediateActionGestureRecognizer.get()]);
3707         [_data->_immediateActionGestureRecognizer setDelegate:_data->_immediateActionController.get()];
3708         [_data->_immediateActionGestureRecognizer setDelaysPrimaryMouseButtonEvents:NO];
3709     }
3710 #endif
3711
3712     return self;
3713 }
3714
3715 - (void)_registerDraggedTypes
3716 {
3717     NSMutableSet *types = [[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()];
3718     [types addObjectsFromArray:PasteboardTypes::forURL()];
3719     [self registerForDraggedTypes:[types allObjects]];
3720     [types release];
3721 }
3722
3723 #if WK_API_ENABLED
3724 - (void)_setThumbnailView:(_WKThumbnailView *)thumbnailView
3725 {
3726     ASSERT(!_data->_thumbnailView || !thumbnailView);
3727
3728     _data->_thumbnailView = thumbnailView;
3729
3730     if (thumbnailView)
3731         [self _updateThumbnailViewLayer];
3732     else
3733         [self _setAcceleratedCompositingModeRootLayer:_data->_rootLayer.get()];
3734 }
3735
3736 - (_WKThumbnailView *)_thumbnailView
3737 {
3738     return _data->_thumbnailView;
3739 }
3740
3741 - (void)_updateThumbnailViewLayer
3742 {
3743     _WKThumbnailView *thumbnailView = _data->_thumbnailView;
3744     ASSERT(thumbnailView);
3745
3746     if (thumbnailView._waitingForSnapshot && self.window)
3747         [self _reparentLayerTreeInThumbnailView];
3748 }
3749
3750 - (void)_reparentLayerTreeInThumbnailView
3751 {
3752     _data->_thumbnailView._thumbnailLayer = _data->_rootLayer.get();
3753 }
3754 #endif // WK_API_ENABLED
3755
3756 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
3757
3758 - (void)_updateContentInsetsIfAutomatic
3759 {
3760     if (!self._automaticallyAdjustsContentInsets)
3761         return;
3762
3763     if ((self.window.styleMask & NSFullSizeContentViewWindowMask) && !self.window.titlebarAppearsTransparent && ![self enclosingScrollView]) {
3764         NSRect contentLayoutRect = [self convertRect:self.window.contentLayoutRect fromView:nil];
3765         CGFloat newTopContentInset = NSMaxY(contentLayoutRect) - NSHeight(contentLayoutRect);
3766         if (self._topContentInset != newTopContentInset)
3767             self._topContentInset = newTopContentInset;
3768     } else
3769         self._topContentInset = 0;
3770 }
3771
3772 #endif
3773
3774 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
3775 {
3776     if ([NSFontPanel sharedFontPanelExists] && object == [NSFontPanel sharedFontPanel]) {
3777         [self updateFontPanelIfNeeded];
3778         return;
3779     }
3780 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
3781     if (context == keyValueObservingContext) {
3782         if ([keyPath isEqualToString:@"contentLayoutRect"] || [keyPath isEqualToString:@"titlebarAppearsTransparent"])
3783             [self _updateContentInsetsIfAutomatic];
3784         return;
3785     }
3786
3787     [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
3788 #endif
3789 }
3790
3791
3792 - (void)_didFirstVisuallyNonEmptyLayoutForMainFrame
3793 {
3794     if (_data->_gestureController)
3795         _data->_gestureController->didFirstVisuallyNonEmptyLayoutForMainFrame();
3796 }
3797
3798 - (void)_didFinishLoadForMainFrame
3799 {
3800     if (_data->_gestureController)
3801         _data->_gestureController->didFinishLoadForMainFrame();
3802 }
3803
3804 - (void)_didSameDocumentNavigationForMainFrame:(SameDocumentNavigationType)type
3805 {
3806     if (_data->_gestureController)
3807         _data->_gestureController->didSameDocumentNavigationForMainFrame(type);
3808 }
3809
3810 - (void)_removeNavigationGestureSnapshot
3811 {
3812     if (_data->_gestureController)
3813         _data->_gestureController->removeSwipeSnapshot();
3814 }
3815
3816 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
3817
3818 - (void)prepareForMenu:(NSMenu *)menu withEvent:(NSEvent *)event
3819 {
3820     if (_data->_ignoresNonWheelEvents) {
3821         [menu cancelTracking];
3822         return;
3823     }
3824
3825     [_data->_actionMenuController prepareForMenu:menu withEvent:event];
3826 }
3827
3828 - (void)willOpenMenu:(NSMenu *)menu withEvent:(NSEvent *)event
3829 {
3830     if (_data->_ignoresNonWheelEvents) {
3831         [menu cancelTracking];
3832         return;
3833     }
3834
3835     [_data->_actionMenuController willOpenMenu:menu withEvent:event];
3836 }
3837
3838 - (void)didCloseMenu:(NSMenu *)menu withEvent:(NSEvent *)event
3839 {
3840     if (_data->_ignoresNonWheelEvents) {
3841         [menu cancelTracking];
3842         return;
3843     }
3844
3845     [_data->_actionMenuController didCloseMenu:menu withEvent:event];
3846 }
3847
3848 - (void)_didPerformActionMenuHitTest:(const ActionMenuHitTestResult&)hitTestResult forImmediateAction:(BOOL)forImmediateAction userData:(API::Object*)userData
3849 {
3850     if (forImmediateAction)
3851         [_data->_immediateActionController didPerformActionMenuHitTest:hitTestResult userData:userData];
3852     else
3853         [_data->_actionMenuController didPerformActionMenuHitTest:hitTestResult userData:userData];
3854 }
3855
3856 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
3857
3858 @end
3859
3860 @implementation WKView (Private)
3861
3862 - (void)saveBackForwardSnapshotForCurrentItem
3863 {
3864     _data->_page->recordNavigationSnapshot();
3865 }
3866
3867 - (void)saveBackForwardSnapshotForItem:(WKBackForwardListItemRef)item
3868 {
3869     _data->_page->recordNavigationSnapshot(*toImpl(item));
3870 }
3871
3872 - (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef
3873 {
3874     return [self initWithFrame:frame contextRef:contextRef pageGroupRef:pageGroupRef relatedToPage:nil];
3875 }
3876
3877 - (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef relatedToPage:(WKPageRef)relatedPage
3878 {
3879     WebPageConfiguration webPageConfiguration;
3880     webPageConfiguration.pageGroup = toImpl(pageGroupRef);
3881     webPageConfiguration.relatedPage = toImpl(relatedPage);
3882
3883     return [self initWithFrame:frame processPool:*toImpl(contextRef) configuration:webPageConfiguration webView:nil];
3884 }
3885
3886 - (id)initWithFrame:(NSRect)frame configurationRef:(WKPageConfigurationRef)configuration
3887 {
3888     auto& processPool = *toImpl(configuration)->processPool();
3889     auto webPageConfiguration = toImpl(configuration)->webPageConfiguration();
3890
3891     return [self initWithFrame:frame processPool:processPool configuration:webPageConfiguration webView:nil];
3892 }
3893
3894 - (BOOL)wantsUpdateLayer
3895 {
3896     return YES;
3897 }
3898
3899 - (void)updateLayer
3900 {
3901     if ([self drawsBackground] && ![self drawsTransparentBackground])
3902         self.layer.backgroundColor = CGColorGetConstantColor(kCGColorWhite);
3903     else
3904         self.layer.backgroundColor = CGColorGetConstantColor(kCGColorClear);
3905
3906     // If asynchronous geometry updates have been sent by forceAsyncDrawingAreaSizeUpdate,
3907     // then subsequent calls to setFrameSize should not result in us waiting for the did
3908     // udpate response if setFrameSize is called.
3909     if ([self frameSizeUpdatesDisabled])
3910         return;
3911
3912     if (DrawingAreaProxy* drawingArea = _data->_page->drawingArea())
3913         drawingArea->waitForPossibleGeometryUpdate();
3914 }
3915
3916 - (WKPageRef)pageRef
3917 {
3918     return toAPI(_data->_page.get());
3919 }
3920
3921 - (BOOL)canChangeFrameLayout:(WKFrameRef)frameRef
3922 {
3923     // PDF documents are already paginated, so we can't change them to add headers and footers.
3924     return !toImpl(frameRef)->isDisplayingPDFDocument();
3925 }
3926
3927 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo forFrame:(WKFrameRef)frameRef
3928 {
3929     LOG(View, "Creating an NSPrintOperation for frame '%s'", toImpl(frameRef)->url().utf8().data());
3930
3931     // FIXME: If the frame cannot be printed (e.g. if it contains an encrypted PDF that disallows
3932     // printing), this function should return nil.
3933     RetainPtr<WKPrintingView> printingView = adoptNS([[WKPrintingView alloc] initWithFrameProxy:toImpl(frameRef) view:self]);
3934     // NSPrintOperation takes ownership of the view.
3935     NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get() printInfo:printInfo];
3936     [printOperation setCanSpawnSeparateThread:YES];
3937     [printOperation setJobTitle:toImpl(frameRef)->title()];
3938     printingView->_printOperation = printOperation;
3939     return printOperation;
3940 }
3941
3942 - (void)setFrame:(NSRect)rect andScrollBy:(NSSize)offset
3943 {
3944     ASSERT(NSEqualSizes(_data->_resizeScrollOffset, NSZeroSize));
3945
3946     _data->_resizeScrollOffset = offset;
3947     [self setFrame:rect];
3948 }
3949
3950 - (void)disableFrameSizeUpdates
3951 {
3952     _data->_frameSizeUpdatesDisabledCount++;
3953 }
3954
3955 - (void)enableFrameSizeUpdates
3956 {
3957     if (!_data->_frameSizeUpdatesDisabledCount)
3958         return;
3959     
3960     if (!(--_data->_frameSizeUpdatesDisabledCount)) {
3961         if (_data->_clipsToVisibleRect)
3962             [self _updateViewExposedRect];
3963         [self _setDrawingAreaSize:[self frame].size];
3964     }
3965 }
3966
3967 - (BOOL)frameSizeUpdatesDisabled
3968 {
3969     return _data->_frameSizeUpdatesDisabledCount > 0;
3970 }
3971
3972 + (void)hideWordDefinitionWindow
3973 {
3974     if (!getLULookupDefinitionModuleClass())
3975         return;
3976     [getLULookupDefinitionModuleClass() hideDefinition];
3977 }
3978
3979 - (CGFloat)minimumLayoutWidth
3980 {
3981     static BOOL loggedDeprecationWarning = NO;
3982
3983     if (!loggedDeprecationWarning) {
3984         NSLog(@"Please use minimumSizeForAutoLayout instead of minimumLayoutWidth.");
3985         loggedDeprecationWarning = YES;
3986     }
3987
3988     return self.minimumSizeForAutoLayout.width;
3989 }
3990
3991 - (void)setMinimumLayoutWidth:(CGFloat)minimumLayoutWidth
3992 {
3993     static BOOL loggedDeprecationWarning = NO;
3994
3995     if (!loggedDeprecationWarning) {
3996         NSLog(@"Please use setMinimumSizeForAutoLayout: instead of setMinimumLayoutWidth:.");
3997         loggedDeprecationWarning = YES;
3998     }
3999
4000     [self setMinimumWidthForAutoLayout:minimumLayoutWidth];
4001 }
4002
4003 - (CGFloat)minimumWidthForAutoLayout
4004 {
4005     return self.minimumSizeForAutoLayout.width;
4006 }
4007
4008 - (void)setMinimumWidthForAutoLayout:(CGFloat)minimumLayoutWidth
4009 {
4010     self.minimumSizeForAutoLayout = NSMakeSize(minimumLayoutWidth, self.minimumSizeForAutoLayout.height);
4011 }
4012
4013 - (NSSize)minimumSizeForAutoLayout
4014 {
4015     return _data->_page->minimumLayoutSize();
4016 }
4017
4018 - (void)setMinimumSizeForAutoLayout:(NSSize)minimumSizeForAutoLayout
4019 {
4020     BOOL expandsToFit = minimumSizeForAutoLayout.width > 0;
4021
4022     _data->_page->setMinimumLayoutSize(IntSize(minimumSizeForAutoLayout.width, minimumSizeForAutoLayout.height));
4023     _data->_page->setMainFrameIsScrollable(!expandsToFit);
4024
4025     [self setShouldClipToVisibleRect:expandsToFit];
4026 }
4027
4028 - (BOOL)shouldExpandToViewHeightForAutoLayout
4029 {
4030     return _data->_page->autoSizingShouldExpandToViewHeight();
4031 }
4032
4033 - (void)setShouldExpandToViewHeightForAutoLayout:(BOOL)shouldExpand
4034 {
4035     return _data->_page->setAutoSizingShouldExpandToViewHeight(shouldExpand);
4036 }
4037
4038 - (BOOL)shouldClipToVisibleRect
4039 {
4040     return _data->_clipsToVisibleRect;
4041 }
4042
4043 - (void)setShouldClipToVisibleRect:(BOOL)clipsToVisibleRect
4044 {
4045     _data->_clipsToVisibleRect = clipsToVisibleRect;
4046     [self _updateViewExposedRect];
4047 }
4048
4049 - (NSColor *)underlayColor
4050 {
4051     Color webColor = _data->_page->underlayColor();
4052     if (!webColor.isValid())
4053         return nil;
4054
4055     return nsColor(webColor);
4056 }
4057
4058 - (void)setUnderlayColor:(NSColor *)underlayColor
4059 {
4060     _data->_page->setUnderlayColor(colorFromNSColor(underlayColor));
4061 }
4062
4063 - (NSView *)fullScreenPlaceholderView
4064 {
4065 #if ENABLE(FULLSCREEN_API)
4066     if (_data->_fullScreenWindowController && [_data->_fullScreenWindowController isFullScreen])
4067         return [_data->_fullScreenWindowController webViewPlaceholder];
4068 #endif
4069     return nil;
4070 }
4071
4072 - (NSWindow *)createFullScreenWindow
4073 {
4074 #if ENABLE(FULLSCREEN_API)
4075     return [[[WebCoreFullScreenWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] styleMask:(NSBorderlessWindowMask | NSResizableWindowMask) backing:NSBackingStoreBuffered defer:NO] autorelease];
4076 #else
4077     return nil;
4078 #endif
4079 }
4080
4081 // FIXME: All of these "DeferringViewInWindowChanges" methods should be able to be removed once clients are weaned off of them.
4082 - (void)beg