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