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