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