A window with a hung tab waits 5s before becoming active
[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::acceptsFirstResponder()
559 {
560     return true;
561 }
562
563 bool WebViewImpl::becomeFirstResponder()
564 {
565     // If we just became first responder again, there is no need to do anything,
566     // since resignFirstResponder has correctly detected this situation.
567     if (m_willBecomeFirstResponderAgain) {
568         m_willBecomeFirstResponderAgain = false;
569         return true;
570     }
571
572     NSSelectionDirection direction = [[m_view window] keyViewSelectionDirection];
573
574     m_inBecomeFirstResponder = true;
575
576     updateSecureInputState();
577     m_page->viewStateDidChange(WebCore::ViewState::IsFocused);
578     // Restore the selection in the editable region if resigning first responder cleared selection.
579     m_page->restoreSelectionInFocusedEditableElement();
580
581     m_inBecomeFirstResponder = false;
582
583     if (direction != NSDirectSelection) {
584         NSEvent *event = [NSApp currentEvent];
585         NSEvent *keyboardEvent = nil;
586         if ([event type] == NSKeyDown || [event type] == NSKeyUp)
587             keyboardEvent = event;
588         m_page->setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, { }), [](WebKit::CallbackBase::Error) { });
589     }
590     return true;
591 }
592
593 bool WebViewImpl::resignFirstResponder()
594 {
595 #if WK_API_ENABLED
596     // Predict the case where we are losing first responder status only to
597     // gain it back again. We want resignFirstResponder to do nothing in that case.
598     id nextResponder = [[m_view window] _newFirstResponderAfterResigning];
599
600     // FIXME: This will probably need to change once WKWebView doesn't contain a WKView.
601     if ([nextResponder isKindOfClass:[WKWebView class]] && m_view.superview == nextResponder) {
602         m_willBecomeFirstResponderAgain = true;
603         return true;
604     }
605 #endif
606
607     m_willBecomeFirstResponderAgain = false;
608     m_inResignFirstResponder = true;
609
610 #if USE(ASYNC_NSTEXTINPUTCLIENT)
611     m_page->confirmCompositionAsync();
612 #else
613     if (m_page->editorState().hasComposition && !m_page->editorState().shouldIgnoreCompositionSelectionChange)
614         m_page->cancelComposition();
615 #endif
616
617     notifyInputContextAboutDiscardedComposition();
618
619     resetSecureInputState();
620
621     if (!m_page->maintainsInactiveSelection())
622         m_page->clearSelection();
623
624     m_page->viewStateDidChange(WebCore::ViewState::IsFocused);
625     
626     m_inResignFirstResponder = false;
627     
628     return true;
629 }
630
631 bool WebViewImpl::isFocused() const
632 {
633     if (m_inBecomeFirstResponder)
634         return true;
635     if (m_inResignFirstResponder)
636         return false;
637     return m_view.window.firstResponder == m_view;
638 }
639
640 void WebViewImpl::viewWillStartLiveResize()
641 {
642     m_page->viewWillStartLiveResize();
643
644     [m_layoutStrategy willStartLiveResize];
645 }
646
647 void WebViewImpl::viewDidEndLiveResize()
648 {
649     m_page->viewWillEndLiveResize();
650
651     [m_layoutStrategy didEndLiveResize];
652 }
653
654 void WebViewImpl::renewGState()
655 {
656     if (m_textIndicatorWindow)
657         dismissContentRelativeChildWindowsWithAnimation(false);
658
659     // Update the view frame.
660     if (m_view.window)
661         updateWindowAndViewFrames();
662
663     updateContentInsetsIfAutomatic();
664 }
665
666 void WebViewImpl::setFrameSize(CGSize)
667 {
668     [m_layoutStrategy didChangeFrameSize];
669 }
670
671 void WebViewImpl::disableFrameSizeUpdates()
672 {
673     [m_layoutStrategy disableFrameSizeUpdates];
674 }
675
676 void WebViewImpl::enableFrameSizeUpdates()
677 {
678     [m_layoutStrategy enableFrameSizeUpdates];
679 }
680
681 bool WebViewImpl::frameSizeUpdatesDisabled() const
682 {
683     return [m_layoutStrategy frameSizeUpdatesDisabled];
684 }
685
686 void WebViewImpl::setFrameAndScrollBy(CGRect frame, CGSize offset)
687 {
688     ASSERT(CGSizeEqualToSize(m_resizeScrollOffset, CGSizeZero));
689
690     m_resizeScrollOffset = offset;
691     m_view.frame = NSRectFromCGRect(frame);
692 }
693
694 void WebViewImpl::updateWindowAndViewFrames()
695 {
696     if (clipsToVisibleRect())
697         updateViewExposedRect();
698
699     if (m_didScheduleWindowAndViewFrameUpdate)
700         return;
701
702     m_didScheduleWindowAndViewFrameUpdate = true;
703
704     auto weakThis = createWeakPtr();
705     dispatch_async(dispatch_get_main_queue(), [weakThis] {
706         if (!weakThis)
707             return;
708
709         weakThis->m_didScheduleWindowAndViewFrameUpdate = false;
710
711         NSRect viewFrameInWindowCoordinates = NSZeroRect;
712         NSPoint accessibilityPosition = NSZeroPoint;
713
714         if (weakThis->m_needsViewFrameInWindowCoordinates)
715             viewFrameInWindowCoordinates = [weakThis->m_view convertRect:weakThis->m_view.frame toView:nil];
716
717 #pragma clang diagnostic push
718 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
719         if (WebCore::AXObjectCache::accessibilityEnabled())
720             accessibilityPosition = [[weakThis->m_view accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
721 #pragma clang diagnostic pop
722         
723         weakThis->m_page->windowAndViewFramesChanged(viewFrameInWindowCoordinates, accessibilityPosition);
724     });
725 }
726
727 void WebViewImpl::setFixedLayoutSize(CGSize fixedLayoutSize)
728 {
729     m_page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
730 }
731
732 CGSize WebViewImpl::fixedLayoutSize() const
733 {
734     return m_page->fixedLayoutSize();
735 }
736
737 std::unique_ptr<WebKit::DrawingAreaProxy> WebViewImpl::createDrawingAreaProxy()
738 {
739     if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"WebKit2UseRemoteLayerTreeDrawingArea"] boolValue])
740         return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(m_page);
741
742     return std::make_unique<TiledCoreAnimationDrawingAreaProxy>(m_page);
743 }
744
745 bool WebViewImpl::isUsingUISideCompositing() const
746 {
747     auto* drawingArea = m_page->drawingArea();
748     return drawingArea && drawingArea->type() == DrawingAreaTypeRemoteLayerTree;
749 }
750
751 void WebViewImpl::setDrawingAreaSize(CGSize size)
752 {
753     if (!m_page->drawingArea())
754         return;
755
756     m_page->drawingArea()->setSize(WebCore::IntSize(size), WebCore::IntSize(), WebCore::IntSize(m_resizeScrollOffset));
757     m_resizeScrollOffset = CGSizeZero;
758 }
759
760 #pragma clang diagnostic push
761 #pragma clang diagnostic ignored "-Wmissing-noreturn"
762 // This method forces a drawing area geometry update, even if frame size updates are disabled.
763 // The updated is performed asynchronously; we don't wait for the geometry update before returning.
764 // The area drawn need not match the current frame size - if it differs it will be anchored to the
765 // frame according to the current contentAnchor.
766 void WebViewImpl::forceAsyncDrawingAreaSizeUpdate(CGSize size)
767 {
768     // This SPI is only used on 10.9 and below, and is incompatible with the fence-based drawing area size synchronization in 10.10+.
769 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
770     if (m_clipsToVisibleRect)
771         updateViewExposedRect();
772     setDrawingAreaSize(size);
773
774     // If a geometry update is pending the new update won't be sent. Poll without waiting for any
775     // pending did-update message now, such that the new update can be sent. We do so after setting
776     // the drawing area size such that the latest update is sent.
777     if (DrawingAreaProxy* drawingArea = m_page->drawingArea())
778         drawingArea->waitForPossibleGeometryUpdate(std::chrono::milliseconds::zero());
779 #else
780     ASSERT_NOT_REACHED();
781 #endif
782 }
783
784 void WebViewImpl::waitForAsyncDrawingAreaSizeUpdate()
785 {
786     // This SPI is only used on 10.9 and below, and is incompatible with the fence-based drawing area size synchronization in 10.10+.
787 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
788     if (DrawingAreaProxy* drawingArea = m_page->drawingArea()) {
789         // If a geometry update is still pending then the action of receiving the
790         // first geometry update may result in another update being scheduled -
791         // we should wait for this to complete too.
792         drawingArea->waitForPossibleGeometryUpdate(DrawingAreaProxy::didUpdateBackingStoreStateTimeout() / 2);
793         drawingArea->waitForPossibleGeometryUpdate(DrawingAreaProxy::didUpdateBackingStoreStateTimeout() / 2);
794     }
795 #else
796     ASSERT_NOT_REACHED();
797 #endif
798 }
799 #pragma clang diagnostic pop
800
801 void WebViewImpl::updateLayer()
802 {
803     m_view.layer.backgroundColor = CGColorGetConstantColor(drawsBackground() ? kCGColorWhite : kCGColorClear);
804
805     // If asynchronous geometry updates have been sent by forceAsyncDrawingAreaSizeUpdate,
806     // then subsequent calls to setFrameSize should not result in us waiting for the did
807     // udpate response if setFrameSize is called.
808     if (frameSizeUpdatesDisabled())
809         return;
810
811     if (DrawingAreaProxy* drawingArea = m_page->drawingArea())
812         drawingArea->waitForPossibleGeometryUpdate();
813 }
814
815 void WebViewImpl::drawRect(CGRect rect)
816 {
817     LOG(Printing, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
818     m_page->endPrinting();
819 }
820
821 bool WebViewImpl::canChangeFrameLayout(WebFrameProxy& frame)
822 {
823     // PDF documents are already paginated, so we can't change them to add headers and footers.
824     return !frame.isDisplayingPDFDocument();
825 }
826
827 NSPrintOperation *WebViewImpl::printOperationWithPrintInfo(NSPrintInfo *printInfo, WebFrameProxy& frame)
828 {
829     LOG(Printing, "Creating an NSPrintOperation for frame '%s'", frame.url().utf8().data());
830
831     // FIXME: If the frame cannot be printed (e.g. if it contains an encrypted PDF that disallows
832     // printing), this function should return nil.
833     RetainPtr<WKPrintingView> printingView = adoptNS([[WKPrintingView alloc] initWithFrameProxy:&frame view:m_view]);
834     // NSPrintOperation takes ownership of the view.
835     NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get() printInfo:printInfo];
836     [printOperation setCanSpawnSeparateThread:YES];
837     [printOperation setJobTitle:frame.title()];
838     printingView->_printOperation = printOperation;
839     return printOperation;
840 }
841
842 void WebViewImpl::setAutomaticallyAdjustsContentInsets(bool automaticallyAdjustsContentInsets)
843 {
844     m_automaticallyAdjustsContentInsets = automaticallyAdjustsContentInsets;
845     updateContentInsetsIfAutomatic();
846 }
847
848 void WebViewImpl::updateContentInsetsIfAutomatic()
849 {
850 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
851     if (!m_automaticallyAdjustsContentInsets)
852         return;
853
854     NSWindow *window = m_view.window;
855     if ((window.styleMask & NSFullSizeContentViewWindowMask) && !window.titlebarAppearsTransparent && ![m_view enclosingScrollView]) {
856         NSRect contentLayoutRect = [m_view convertRect:window.contentLayoutRect fromView:nil];
857         CGFloat newTopContentInset = NSMaxY(contentLayoutRect) - NSHeight(contentLayoutRect);
858         if (m_topContentInset != newTopContentInset)
859             setTopContentInset(newTopContentInset);
860     } else
861         setTopContentInset(0);
862 #endif
863 }
864
865 void WebViewImpl::setTopContentInset(CGFloat contentInset)
866 {
867     m_topContentInset = contentInset;
868
869     if (m_didScheduleSetTopContentInset)
870         return;
871
872     m_didScheduleSetTopContentInset = true;
873
874     auto weakThis = createWeakPtr();
875     dispatch_async(dispatch_get_main_queue(), [weakThis] {
876         if (!weakThis)
877             return;
878         weakThis->dispatchSetTopContentInset();
879     });
880 }
881
882 void WebViewImpl::dispatchSetTopContentInset()
883 {
884     if (!m_didScheduleSetTopContentInset)
885         return;
886
887     m_didScheduleSetTopContentInset = false;
888     m_page->setTopContentInset(m_topContentInset);
889 }
890
891 void WebViewImpl::prepareContentInRect(CGRect rect)
892 {
893     m_contentPreparationRect = rect;
894     m_useContentPreparationRectForVisibleRect = true;
895
896     updateViewExposedRect();
897 }
898
899 void WebViewImpl::updateViewExposedRect()
900 {
901     CGRect exposedRect = NSRectToCGRect([m_view visibleRect]);
902
903     if (m_useContentPreparationRectForVisibleRect)
904         exposedRect = CGRectUnion(m_contentPreparationRect, exposedRect);
905
906     if (auto drawingArea = m_page->drawingArea())
907         drawingArea->setExposedRect(m_clipsToVisibleRect ? WebCore::FloatRect(exposedRect) : WebCore::FloatRect::infiniteRect());
908 }
909
910 void WebViewImpl::setClipsToVisibleRect(bool clipsToVisibleRect)
911 {
912     m_clipsToVisibleRect = clipsToVisibleRect;
913     updateViewExposedRect();
914 }
915
916 void WebViewImpl::setMinimumSizeForAutoLayout(CGSize minimumSizeForAutoLayout)
917 {
918     bool expandsToFit = minimumSizeForAutoLayout.width > 0;
919
920     m_page->setMinimumLayoutSize(WebCore::IntSize(minimumSizeForAutoLayout));
921     m_page->setMainFrameIsScrollable(!expandsToFit);
922
923     setClipsToVisibleRect(expandsToFit);
924 }
925
926 CGSize WebViewImpl::minimumSizeForAutoLayout() const
927 {
928     return m_page->minimumLayoutSize();
929 }
930
931 void WebViewImpl::setShouldExpandToViewHeightForAutoLayout(bool shouldExpandToViewHeightForAutoLayout)
932 {
933     m_page->setAutoSizingShouldExpandToViewHeight(shouldExpandToViewHeightForAutoLayout);
934 }
935
936 bool WebViewImpl::shouldExpandToViewHeightForAutoLayout() const
937 {
938     return m_page->autoSizingShouldExpandToViewHeight();
939 }
940
941 void WebViewImpl::setIntrinsicContentSize(CGSize intrinsicContentSize)
942 {
943     // If the intrinsic content size is less than the minimum layout width, the content flowed to fit,
944     // so we can report that that dimension is flexible. If not, we need to report our intrinsic width
945     // so that autolayout will know to provide space for us.
946
947     CGSize intrinsicContentSizeAcknowledgingFlexibleWidth = intrinsicContentSize;
948     if (intrinsicContentSize.width < m_page->minimumLayoutSize().width())
949         intrinsicContentSizeAcknowledgingFlexibleWidth.width = NSViewNoInstrinsicMetric;
950
951     m_intrinsicContentSize = intrinsicContentSizeAcknowledgingFlexibleWidth;
952     [m_view invalidateIntrinsicContentSize];
953 }
954
955 CGSize WebViewImpl::intrinsicContentSize() const
956 {
957     return m_intrinsicContentSize;
958 }
959
960 void WebViewImpl::setViewScale(CGFloat viewScale)
961 {
962     m_lastRequestedViewScale = viewScale;
963
964     if (!supportsArbitraryLayoutModes() && viewScale != 1)
965         return;
966
967     if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
968         [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
969
970     m_page->scaleView(viewScale);
971     [m_layoutStrategy didChangeViewScale];
972 }
973
974 CGFloat WebViewImpl::viewScale() const
975 {
976     return m_page->viewScaleFactor();
977 }
978
979 WKLayoutMode WebViewImpl::layoutMode() const
980 {
981     return [m_layoutStrategy layoutMode];
982 }
983
984 void WebViewImpl::setLayoutMode(WKLayoutMode layoutMode)
985 {
986     m_lastRequestedLayoutMode = layoutMode;
987
988     if (!supportsArbitraryLayoutModes() && layoutMode != kWKLayoutModeViewSize)
989         return;
990
991     if (layoutMode == [m_layoutStrategy layoutMode])
992         return;
993
994     [m_layoutStrategy willChangeLayoutStrategy];
995     m_layoutStrategy = [WKViewLayoutStrategy layoutStrategyWithPage:m_page view:m_view viewImpl:*this mode:layoutMode];
996 }
997
998 bool WebViewImpl::supportsArbitraryLayoutModes() const
999 {
1000     if ([m_fullScreenWindowController isFullScreen])
1001         return false;
1002
1003     WebFrameProxy* frame = m_page->mainFrame();
1004     if (!frame)
1005         return true;
1006
1007     // If we have a plugin document in the main frame, avoid using custom WKLayoutModes
1008     // and fall back to the defaults, because there's a good chance that it won't work (e.g. with PDFPlugin).
1009     if (frame->containsPluginDocument())
1010         return false;
1011
1012     return true;
1013 }
1014
1015 void WebViewImpl::updateSupportsArbitraryLayoutModes()
1016 {
1017     if (!supportsArbitraryLayoutModes()) {
1018         WKLayoutMode oldRequestedLayoutMode = m_lastRequestedLayoutMode;
1019         CGFloat oldRequestedViewScale = m_lastRequestedViewScale;
1020         setViewScale(1);
1021         setLayoutMode(kWKLayoutModeViewSize);
1022
1023         // The 'last requested' parameters will have been overwritten by setting them above, but we don't
1024         // want this to count as a request (only changes from the client count), so reset them.
1025         m_lastRequestedLayoutMode = oldRequestedLayoutMode;
1026         m_lastRequestedViewScale = oldRequestedViewScale;
1027     } else if (m_lastRequestedLayoutMode != [m_layoutStrategy layoutMode]) {
1028         setViewScale(m_lastRequestedViewScale);
1029         setLayoutMode(m_lastRequestedLayoutMode);
1030     }
1031 }
1032
1033 void WebViewImpl::setOverrideDeviceScaleFactor(CGFloat deviceScaleFactor)
1034 {
1035     m_overrideDeviceScaleFactor = deviceScaleFactor;
1036     m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1037 }
1038
1039 float WebViewImpl::intrinsicDeviceScaleFactor() const
1040 {
1041     if (m_overrideDeviceScaleFactor)
1042         return m_overrideDeviceScaleFactor;
1043     if (m_targetWindowForMovePreparation)
1044         return m_targetWindowForMovePreparation.backingScaleFactor;
1045     if (NSWindow *window = m_view.window)
1046         return window.backingScaleFactor;
1047     return [NSScreen mainScreen].backingScaleFactor;
1048 }
1049
1050 void WebViewImpl::windowDidOrderOffScreen()
1051 {
1052     m_page->viewStateDidChange(WebCore::ViewState::IsVisible | WebCore::ViewState::WindowIsActive);
1053 }
1054
1055 void WebViewImpl::windowDidOrderOnScreen()
1056 {
1057     m_page->viewStateDidChange(WebCore::ViewState::IsVisible | WebCore::ViewState::WindowIsActive);
1058 }
1059
1060 void WebViewImpl::windowDidBecomeKey(NSWindow *keyWindow)
1061 {
1062     if (keyWindow == m_view.window || keyWindow == m_view.window.attachedSheet) {
1063         updateSecureInputState();
1064         m_page->viewStateDidChange(WebCore::ViewState::WindowIsActive);
1065     }
1066 }
1067
1068 void WebViewImpl::windowDidResignKey(NSWindow *formerKeyWindow)
1069 {
1070     if (formerKeyWindow == m_view.window || formerKeyWindow == m_view.window.attachedSheet) {
1071         updateSecureInputState();
1072         m_page->viewStateDidChange(WebCore::ViewState::WindowIsActive);
1073     }
1074 }
1075
1076 void WebViewImpl::windowDidMiniaturize()
1077 {
1078     m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1079 }
1080
1081 void WebViewImpl::windowDidDeminiaturize()
1082 {
1083     m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1084 }
1085
1086 void WebViewImpl::windowDidMove()
1087 {
1088     updateWindowAndViewFrames();
1089 }
1090
1091 void WebViewImpl::windowDidResize()
1092 {
1093     updateWindowAndViewFrames();
1094 }
1095
1096 void WebViewImpl::windowDidChangeBackingProperties(CGFloat oldBackingScaleFactor)
1097 {
1098     CGFloat newBackingScaleFactor = intrinsicDeviceScaleFactor();
1099     if (oldBackingScaleFactor == newBackingScaleFactor)
1100         return;
1101
1102     m_page->setIntrinsicDeviceScaleFactor(newBackingScaleFactor);
1103 }
1104
1105 void WebViewImpl::windowDidChangeScreen()
1106 {
1107     NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1108     m_page->windowScreenDidChange((PlatformDisplayID)[[[[window screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue]);
1109 }
1110
1111 void WebViewImpl::windowDidChangeLayerHosting()
1112 {
1113     m_page->layerHostingModeDidChange();
1114 }
1115
1116 void WebViewImpl::windowDidChangeOcclusionState()
1117 {
1118     m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1119 }
1120
1121 bool WebViewImpl::mightBeginDragWhileInactive()
1122 {
1123     if (m_view.window.isKeyWindow)
1124         return false;
1125
1126     if (m_page->editorState().selectionIsNone || !m_page->editorState().selectionIsRange)
1127         return false;
1128
1129     return true;
1130 }
1131
1132 bool WebViewImpl::mightBeginScrollWhileInactive()
1133 {
1134     // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
1135     if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy)
1136         return true;
1137
1138     return false;
1139 }
1140
1141 bool WebViewImpl::acceptsFirstMouse(NSEvent *event)
1142 {
1143     if (!mightBeginDragWhileInactive() && !mightBeginScrollWhileInactive())
1144         return false;
1145
1146     // There's a chance that responding to this event will run a nested event loop, and
1147     // fetching a new event might release the old one. Retaining and then autoreleasing
1148     // the current event prevents that from causing a problem inside WebKit or AppKit code.
1149     [[event retain] autorelease];
1150
1151     if (![m_view hitTest:event.locationInWindow])
1152         return false;
1153
1154     setLastMouseDownEvent(event);
1155     bool result = m_page->acceptsFirstMouse(event.eventNumber, WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
1156     setLastMouseDownEvent(nil);
1157     return result;
1158 }
1159
1160 bool WebViewImpl::shouldDelayWindowOrderingForEvent(NSEvent *event)
1161 {
1162     if (!mightBeginDragWhileInactive())
1163         return false;
1164
1165     // There's a chance that responding to this event will run a nested event loop, and
1166     // fetching a new event might release the old one. Retaining and then autoreleasing
1167     // the current event prevents that from causing a problem inside WebKit or AppKit code.
1168     [[event retain] autorelease];
1169
1170     if (![m_view hitTest:event.locationInWindow])
1171         return false;
1172
1173     setLastMouseDownEvent(event);
1174     bool result = m_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
1175     setLastMouseDownEvent(nil);
1176     return result;
1177 }
1178
1179 bool WebViewImpl::windowResizeMouseLocationIsInVisibleScrollerThumb(CGPoint point)
1180 {
1181     NSPoint localPoint = [m_view convertPoint:NSPointFromCGPoint(point) fromView:nil];
1182     NSRect visibleThumbRect = NSRect(m_page->visibleScrollerThumbRect());
1183     return NSMouseInRect(localPoint, visibleThumbRect, m_view.isFlipped);
1184 }
1185
1186 void WebViewImpl::viewWillMoveToWindow(NSWindow *window)
1187 {
1188     // If we're in the middle of preparing to move to a window, we should only be moved to that window.
1189     ASSERT(!m_targetWindowForMovePreparation || (m_targetWindowForMovePreparation == window));
1190
1191     NSWindow *currentWindow = m_view.window;
1192     if (window == currentWindow)
1193         return;
1194
1195     clearAllEditCommands();
1196
1197     NSWindow *stopObservingWindow = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1198     [m_windowVisibilityObserver stopObserving:stopObservingWindow];
1199     [m_windowVisibilityObserver startObserving:window];
1200 }
1201
1202 void WebViewImpl::viewDidMoveToWindow()
1203 {
1204     NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1205
1206     if (window) {
1207         windowDidChangeScreen();
1208
1209         WebCore::ViewState::Flags viewStateChanges = WebCore::ViewState::WindowIsActive | WebCore::ViewState::IsVisible;
1210         if (m_isDeferringViewInWindowChanges)
1211             m_viewInWindowChangeWasDeferred = true;
1212         else
1213             viewStateChanges |= WebCore::ViewState::IsInWindow;
1214         m_page->viewStateDidChange(viewStateChanges);
1215
1216         updateWindowAndViewFrames();
1217
1218         // FIXME(135509) This call becomes unnecessary once 135509 is fixed; remove.
1219         m_page->layerHostingModeDidChange();
1220
1221         if (!m_flagsChangedEventMonitor) {
1222             auto weakThis = createWeakPtr();
1223             m_flagsChangedEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSFlagsChangedMask handler:[weakThis] (NSEvent *flagsChangedEvent) {
1224                 if (weakThis)
1225                     weakThis->postFakeMouseMovedEventForFlagsChangedEvent(flagsChangedEvent);
1226                 return flagsChangedEvent;
1227             }];
1228         }
1229
1230         accessibilityRegisterUIProcessTokens();
1231
1232 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1233         if (m_immediateActionGestureRecognizer && ![[m_view gestureRecognizers] containsObject:m_immediateActionGestureRecognizer.get()] && !m_ignoresNonWheelEvents && m_allowsLinkPreview)
1234             [m_view addGestureRecognizer:m_immediateActionGestureRecognizer.get()];
1235 #endif
1236     } else {
1237         WebCore::ViewState::Flags viewStateChanges = WebCore::ViewState::WindowIsActive | WebCore::ViewState::IsVisible;
1238         if (m_isDeferringViewInWindowChanges)
1239             m_viewInWindowChangeWasDeferred = true;
1240         else
1241             viewStateChanges |= WebCore::ViewState::IsInWindow;
1242         m_page->viewStateDidChange(viewStateChanges);
1243
1244         [NSEvent removeMonitor:m_flagsChangedEventMonitor];
1245         m_flagsChangedEventMonitor = nil;
1246
1247         dismissContentRelativeChildWindowsWithAnimation(false);
1248
1249 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1250         if (m_immediateActionGestureRecognizer)
1251             [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
1252 #endif
1253     }
1254
1255     m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1256 }
1257
1258 void WebViewImpl::viewDidChangeBackingProperties()
1259 {
1260     NSColorSpace *colorSpace = m_view.window.colorSpace;
1261     if ([colorSpace isEqualTo:m_colorSpace.get()])
1262         return;
1263
1264     m_colorSpace = nullptr;
1265     if (DrawingAreaProxy *drawingArea = m_page->drawingArea())
1266         drawingArea->colorSpaceDidChange();
1267 }
1268
1269 void WebViewImpl::viewDidHide()
1270 {
1271     m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1272 }
1273
1274 void WebViewImpl::viewDidUnhide()
1275 {
1276     m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1277 }
1278
1279 void WebViewImpl::activeSpaceDidChange()
1280 {
1281     m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1282 }
1283
1284 NSView *WebViewImpl::hitTest(CGPoint point)
1285 {
1286     NSView *hitView = [m_view _web_superHitTest:NSPointFromCGPoint(point)];
1287     if (hitView && hitView == m_layerHostingView)
1288         hitView = m_view;
1289
1290     return hitView;
1291 }
1292
1293 void WebViewImpl::postFakeMouseMovedEventForFlagsChangedEvent(NSEvent *flagsChangedEvent)
1294 {
1295     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved location:flagsChangedEvent.window.mouseLocationOutsideOfEventStream
1296         modifierFlags:flagsChangedEvent.modifierFlags timestamp:flagsChangedEvent.timestamp windowNumber:flagsChangedEvent.windowNumber
1297         context:flagsChangedEvent.context eventNumber:0 clickCount:0 pressure:0];
1298     NativeWebMouseEvent webEvent(fakeEvent, m_lastPressureEvent.get(), m_view);
1299     m_page->handleMouseEvent(webEvent);
1300 }
1301
1302 ColorSpaceData WebViewImpl::colorSpace()
1303 {
1304     if (!m_colorSpace) {
1305         if (m_targetWindowForMovePreparation)
1306             m_colorSpace = m_targetWindowForMovePreparation.colorSpace;
1307         else if (NSWindow *window = m_view.window)
1308             m_colorSpace = window.colorSpace;
1309         else
1310             m_colorSpace = [NSScreen mainScreen].colorSpace;
1311     }
1312
1313     ColorSpaceData colorSpaceData;
1314     colorSpaceData.cgColorSpace = [m_colorSpace CGColorSpace];
1315     
1316     return colorSpaceData;
1317 }
1318
1319 void WebViewImpl::setUnderlayColor(NSColor *underlayColor)
1320 {
1321     m_page->setUnderlayColor(WebCore::colorFromNSColor(underlayColor));
1322 }
1323
1324 NSColor *WebViewImpl::underlayColor() const
1325 {
1326     WebCore::Color webColor = m_page->underlayColor();
1327     if (!webColor.isValid())
1328         return nil;
1329
1330     return WebCore::nsColor(webColor);
1331 }
1332
1333 NSColor *WebViewImpl::pageExtendedBackgroundColor() const
1334 {
1335     WebCore::Color color = m_page->pageExtendedBackgroundColor();
1336     if (!color.isValid())
1337         return nil;
1338
1339     return WebCore::nsColor(color);
1340 }
1341
1342 void WebViewImpl::setOverlayScrollbarStyle(WTF::Optional<WebCore::ScrollbarOverlayStyle> scrollbarStyle)
1343 {
1344     m_page->setOverlayScrollbarStyle(scrollbarStyle);
1345 }
1346
1347 WTF::Optional<WebCore::ScrollbarOverlayStyle> WebViewImpl::overlayScrollbarStyle() const
1348 {
1349     return m_page->overlayScrollbarStyle();
1350 }
1351
1352 void WebViewImpl::beginDeferringViewInWindowChanges()
1353 {
1354     if (m_shouldDeferViewInWindowChanges) {
1355         NSLog(@"beginDeferringViewInWindowChanges was called while already deferring view-in-window changes!");
1356         return;
1357     }
1358
1359     m_shouldDeferViewInWindowChanges = true;
1360 }
1361
1362 void WebViewImpl::endDeferringViewInWindowChanges()
1363 {
1364     if (!m_shouldDeferViewInWindowChanges) {
1365         NSLog(@"endDeferringViewInWindowChanges was called without beginDeferringViewInWindowChanges!");
1366         return;
1367     }
1368
1369     m_shouldDeferViewInWindowChanges = false;
1370
1371     if (m_viewInWindowChangeWasDeferred) {
1372         dispatchSetTopContentInset();
1373         m_page->viewStateDidChange(WebCore::ViewState::IsInWindow);
1374         m_viewInWindowChangeWasDeferred = false;
1375     }
1376 }
1377
1378 void WebViewImpl::endDeferringViewInWindowChangesSync()
1379 {
1380     if (!m_shouldDeferViewInWindowChanges) {
1381         NSLog(@"endDeferringViewInWindowChangesSync was called without beginDeferringViewInWindowChanges!");
1382         return;
1383     }
1384
1385     m_shouldDeferViewInWindowChanges = false;
1386
1387     if (m_viewInWindowChangeWasDeferred) {
1388         dispatchSetTopContentInset();
1389         m_page->viewStateDidChange(WebCore::ViewState::IsInWindow);
1390         m_viewInWindowChangeWasDeferred = false;
1391     }
1392 }
1393
1394 void WebViewImpl::prepareForMoveToWindow(NSWindow *targetWindow, std::function<void()> completionHandler)
1395 {
1396     m_shouldDeferViewInWindowChanges = true;
1397     viewWillMoveToWindow(targetWindow);
1398     m_targetWindowForMovePreparation = targetWindow;
1399     viewDidMoveToWindow();
1400
1401     m_shouldDeferViewInWindowChanges = false;
1402
1403     auto weakThis = createWeakPtr();
1404     m_page->installViewStateChangeCompletionHandler([weakThis, completionHandler]() {
1405         completionHandler();
1406
1407         if (!weakThis)
1408             return;
1409
1410         ASSERT(weakThis->m_view.window == weakThis->m_targetWindowForMovePreparation);
1411         weakThis->m_targetWindowForMovePreparation = nil;
1412     });
1413
1414     dispatchSetTopContentInset();
1415     m_page->viewStateDidChange(WebCore::ViewState::IsInWindow, false, WebPageProxy::ViewStateChangeDispatchMode::Immediate);
1416     m_viewInWindowChangeWasDeferred = false;
1417 }
1418
1419 void WebViewImpl::updateSecureInputState()
1420 {
1421     if (![[m_view window] isKeyWindow] || !isFocused()) {
1422         if (m_inSecureInputState) {
1423             DisableSecureEventInput();
1424             m_inSecureInputState = false;
1425         }
1426         return;
1427     }
1428     // WKView has a single input context for all editable areas (except for plug-ins).
1429     NSTextInputContext *context = [m_view _web_superInputContext];
1430     bool isInPasswordField = m_page->editorState().isInPasswordField;
1431
1432     if (isInPasswordField) {
1433         if (!m_inSecureInputState)
1434             EnableSecureEventInput();
1435         static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1];
1436         LOG(TextInput, "-> setAllowedInputSourceLocales:romanInputSources");
1437         [context setAllowedInputSourceLocales:romanInputSources];
1438     } else {
1439         if (m_inSecureInputState)
1440             DisableSecureEventInput();
1441         LOG(TextInput, "-> setAllowedInputSourceLocales:nil");
1442         [context setAllowedInputSourceLocales:nil];
1443     }
1444     m_inSecureInputState = isInPasswordField;
1445 }
1446
1447 void WebViewImpl::resetSecureInputState()
1448 {
1449     if (m_inSecureInputState) {
1450         DisableSecureEventInput();
1451         m_inSecureInputState = false;
1452     }
1453 }
1454
1455 void WebViewImpl::notifyInputContextAboutDiscardedComposition()
1456 {
1457     // <rdar://problem/9359055>: -discardMarkedText can only be called for active contexts.
1458     // FIXME: We fail to ever notify the input context if something (e.g. a navigation) happens while the window is not key.
1459     // This is not a problem when the window is key, because we discard marked text on resigning first responder.
1460     if (![[m_view window] isKeyWindow] || m_view != [[m_view window] firstResponder])
1461         return;
1462
1463     LOG(TextInput, "-> discardMarkedText");
1464
1465     [[m_view _web_superInputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to.
1466 }
1467
1468 void WebViewImpl::setPluginComplexTextInputState(PluginComplexTextInputState pluginComplexTextInputState)
1469 {
1470     m_pluginComplexTextInputState = pluginComplexTextInputState;
1471
1472     if (m_pluginComplexTextInputState != PluginComplexTextInputDisabled)
1473         return;
1474
1475     // Send back an empty string to the plug-in. This will disable text input.
1476     m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, String());
1477 }
1478
1479 void WebViewImpl::setPluginComplexTextInputStateAndIdentifier(PluginComplexTextInputState pluginComplexTextInputState, uint64_t pluginComplexTextInputIdentifier)
1480 {
1481     if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier) {
1482         // We're asked to update the state for a plug-in that doesn't have focus.
1483         return;
1484     }
1485
1486     setPluginComplexTextInputState(pluginComplexTextInputState);
1487 }
1488
1489 void WebViewImpl::disableComplexTextInputIfNecessary()
1490 {
1491     if (!m_pluginComplexTextInputIdentifier)
1492         return;
1493
1494     if (m_pluginComplexTextInputState != PluginComplexTextInputEnabled)
1495         return;
1496
1497     // Check if the text input window has been dismissed.
1498     if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
1499         setPluginComplexTextInputState(PluginComplexTextInputDisabled);
1500 }
1501
1502 bool WebViewImpl::handlePluginComplexTextInputKeyDown(NSEvent *event)
1503 {
1504     ASSERT(m_pluginComplexTextInputIdentifier);
1505     ASSERT(m_pluginComplexTextInputState != PluginComplexTextInputDisabled);
1506
1507     BOOL usingLegacyCocoaTextInput = m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
1508
1509     NSString *string = nil;
1510     BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string];
1511
1512     if (string) {
1513         m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, string);
1514
1515         if (!usingLegacyCocoaTextInput)
1516             m_pluginComplexTextInputState = PluginComplexTextInputDisabled;
1517     }
1518
1519     return didHandleEvent;
1520 }
1521
1522 bool WebViewImpl::tryHandlePluginComplexTextInputKeyDown(NSEvent *event)
1523 {
1524     if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
1525         return NO;
1526
1527     // Check if the text input window has been dismissed and let the plug-in process know.
1528     // This is only valid with the updated Cocoa text input spec.
1529     disableComplexTextInputIfNecessary();
1530
1531     // Try feeding the keyboard event directly to the plug-in.
1532     if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
1533         return handlePluginComplexTextInputKeyDown(event);
1534
1535     return NO;
1536 }
1537
1538 void WebViewImpl::pluginFocusOrWindowFocusChanged(bool pluginHasFocusAndWindowHasFocus, uint64_t pluginComplexTextInputIdentifier)
1539 {
1540     BOOL inputSourceChanged = m_pluginComplexTextInputIdentifier;
1541
1542     if (pluginHasFocusAndWindowHasFocus) {
1543         // Check if we're already allowing text input for this plug-in.
1544         if (pluginComplexTextInputIdentifier == m_pluginComplexTextInputIdentifier)
1545             return;
1546
1547         m_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier;
1548
1549     } else {
1550         // Check if we got a request to unfocus a plug-in that isn't focused.
1551         if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier)
1552             return;
1553
1554         m_pluginComplexTextInputIdentifier = 0;
1555     }
1556
1557     if (inputSourceChanged) {
1558         // The input source changed; discard any entered text.
1559         [[WKTextInputWindowController sharedTextInputWindowController] unmarkText];
1560     }
1561     
1562     // This will force the current input context to be updated to its correct value.
1563     [NSApp updateWindows];
1564 }
1565
1566 bool WebViewImpl::tryPostProcessPluginComplexTextInputKeyDown(NSEvent *event)
1567 {
1568     if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
1569         return NO;
1570
1571     // In the legacy text input model, the event has already been sent to the input method.
1572     if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
1573         return NO;
1574
1575     return handlePluginComplexTextInputKeyDown(event);
1576 }
1577
1578 void WebViewImpl::handleAcceptedAlternativeText(const String& acceptedAlternative)
1579 {
1580     m_page->handleAlternativeTextUIResult(acceptedAlternative);
1581 }
1582
1583
1584 NSInteger WebViewImpl::spellCheckerDocumentTag()
1585 {
1586     if (!m_spellCheckerDocumentTag)
1587         m_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
1588     return m_spellCheckerDocumentTag.value();
1589 }
1590
1591 void WebViewImpl::pressureChangeWithEvent(NSEvent *event)
1592 {
1593 #if defined(__LP64__) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101003
1594     if (event == m_lastPressureEvent)
1595         return;
1596
1597     if (m_ignoresNonWheelEvents)
1598         return;
1599
1600     if (event.phase != NSEventPhaseChanged && event.phase != NSEventPhaseBegan && event.phase != NSEventPhaseEnded)
1601         return;
1602
1603     NativeWebMouseEvent webEvent(event, m_lastPressureEvent.get(), m_view);
1604     m_page->handleMouseEvent(webEvent);
1605
1606     m_lastPressureEvent = event;
1607 #endif
1608 }
1609
1610 #if ENABLE(FULLSCREEN_API)
1611 bool WebViewImpl::hasFullScreenWindowController() const
1612 {
1613     return !!m_fullScreenWindowController;
1614 }
1615
1616 WKFullScreenWindowController *WebViewImpl::fullScreenWindowController()
1617 {
1618     if (!m_fullScreenWindowController)
1619         m_fullScreenWindowController = adoptNS([[WKFullScreenWindowController alloc] initWithWindow:createFullScreenWindow() webView:m_view page:m_page]);
1620
1621     return m_fullScreenWindowController.get();
1622 }
1623
1624 void WebViewImpl::closeFullScreenWindowController()
1625 {
1626     if (!m_fullScreenWindowController)
1627         return;
1628
1629     [m_fullScreenWindowController close];
1630     m_fullScreenWindowController = nullptr;
1631 }
1632 #endif
1633
1634 NSView *WebViewImpl::fullScreenPlaceholderView()
1635 {
1636 #if ENABLE(FULLSCREEN_API)
1637     if (m_fullScreenWindowController && [m_fullScreenWindowController isFullScreen])
1638         return [m_fullScreenWindowController webViewPlaceholder];
1639 #endif
1640     return nil;
1641 }
1642
1643 NSWindow *WebViewImpl::createFullScreenWindow()
1644 {
1645 #if ENABLE(FULLSCREEN_API)
1646     return [[[WebCoreFullScreenWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] styleMask:(NSBorderlessWindowMask | NSResizableWindowMask) backing:NSBackingStoreBuffered defer:NO] autorelease];
1647 #else
1648     return nil;
1649 #endif
1650 }
1651
1652 bool WebViewImpl::isEditable() const
1653 {
1654     return m_page->isEditable();
1655 }
1656
1657 typedef HashMap<SEL, String> SelectorNameMap;
1658
1659 // Map selectors into Editor command names.
1660 // This is not needed for any selectors that have the same name as the Editor command.
1661 static const SelectorNameMap& selectorExceptionMap()
1662 {
1663     static NeverDestroyed<SelectorNameMap> map;
1664
1665     struct SelectorAndCommandName {
1666         SEL selector;
1667         ASCIILiteral commandName;
1668     };
1669
1670     static const SelectorAndCommandName names[] = {
1671         { @selector(insertNewlineIgnoringFieldEditor:), ASCIILiteral("InsertNewline") },
1672         { @selector(insertParagraphSeparator:), ASCIILiteral("InsertNewline") },
1673         { @selector(insertTabIgnoringFieldEditor:), ASCIILiteral("InsertTab") },
1674         { @selector(pageDown:), ASCIILiteral("MovePageDown") },
1675         { @selector(pageDownAndModifySelection:), ASCIILiteral("MovePageDownAndModifySelection") },
1676         { @selector(pageUp:), ASCIILiteral("MovePageUp") },
1677         { @selector(pageUpAndModifySelection:), ASCIILiteral("MovePageUpAndModifySelection") },
1678         { @selector(scrollPageDown:), ASCIILiteral("ScrollPageForward") },
1679         { @selector(scrollPageUp:), ASCIILiteral("ScrollPageBackward") }
1680     };
1681
1682     for (auto& name : names)
1683         map.get().add(name.selector, name.commandName);
1684
1685     return map;
1686 }
1687
1688 static String commandNameForSelector(SEL selector)
1689 {
1690     // Check the exception map first.
1691     static const SelectorNameMap& exceptionMap = selectorExceptionMap();
1692     SelectorNameMap::const_iterator it = exceptionMap.find(selector);
1693     if (it != exceptionMap.end())
1694         return it->value;
1695
1696     // Remove the trailing colon.
1697     // No need to capitalize the command name since Editor command names are
1698     // not case sensitive.
1699     const char* selectorName = sel_getName(selector);
1700     size_t selectorNameLength = strlen(selectorName);
1701     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
1702         return String();
1703     return String(selectorName, selectorNameLength - 1);
1704 }
1705
1706 bool WebViewImpl::executeSavedCommandBySelector(SEL selector)
1707 {
1708     LOG(TextInput, "Executing previously saved command %s", sel_getName(selector));
1709     // The sink does two things: 1) Tells us if the responder went unhandled, and
1710     // 2) prevents any NSBeep; we don't ever want to beep here.
1711     RetainPtr<WKResponderChainSink> sink = adoptNS([[WKResponderChainSink alloc] initWithResponderChain:m_view]);
1712     [m_view _web_superDoCommandBySelector:selector];
1713     [sink detach];
1714     return ![sink didReceiveUnhandledCommand];
1715 }
1716
1717 void WebViewImpl::executeEditCommandForSelector(SEL selector, const String& argument)
1718 {
1719     m_page->executeEditCommand(commandNameForSelector(selector), argument);
1720 }
1721
1722 void WebViewImpl::registerEditCommand(RefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo)
1723 {
1724     RefPtr<WebEditCommandProxy> command = prpCommand;
1725
1726     RetainPtr<WKEditCommandObjC> commandObjC = adoptNS([[WKEditCommandObjC alloc] initWithWebEditCommandProxy:command]);
1727     String actionName = WebEditCommandProxy::nameForEditAction(command->editAction());
1728
1729     NSUndoManager *undoManager = [m_view undoManager];
1730     [undoManager registerUndoWithTarget:m_undoTarget.get() selector:((undoOrRedo == WebPageProxy::Undo) ? @selector(undoEditing:) : @selector(redoEditing:)) object:commandObjC.get()];
1731     if (!actionName.isEmpty())
1732         [undoManager setActionName:(NSString *)actionName];
1733 }
1734
1735 void WebViewImpl::clearAllEditCommands()
1736 {
1737     [[m_view undoManager] removeAllActionsWithTarget:m_undoTarget.get()];
1738 }
1739
1740 bool WebViewImpl::writeSelectionToPasteboard(NSPasteboard *pasteboard, NSArray *types)
1741 {
1742     size_t numTypes = types.count;
1743     [pasteboard declareTypes:types owner:nil];
1744     for (size_t i = 0; i < numTypes; ++i) {
1745         if ([[types objectAtIndex:i] isEqualTo:NSStringPboardType])
1746             [pasteboard setString:m_page->stringSelectionForPasteboard() forType:NSStringPboardType];
1747         else {
1748             RefPtr<WebCore::SharedBuffer> buffer = m_page->dataSelectionForPasteboard([types objectAtIndex:i]);
1749             [pasteboard setData:buffer ? buffer->createNSData().get() : nil forType:[types objectAtIndex:i]];
1750         }
1751     }
1752     return true;
1753 }
1754
1755 bool WebViewImpl::readSelectionFromPasteboard(NSPasteboard *pasteboard)
1756 {
1757     return m_page->readSelectionFromPasteboard([pasteboard name]);
1758 }
1759
1760 id WebViewImpl::validRequestorForSendAndReturnTypes(NSString *sendType, NSString *returnType)
1761 {
1762     EditorState editorState = m_page->editorState();
1763     bool isValidSendType = false;
1764
1765     if (sendType && !editorState.selectionIsNone) {
1766         if (editorState.isInPlugin)
1767             isValidSendType = [sendType isEqualToString:NSStringPboardType];
1768         else
1769             isValidSendType = [PasteboardTypes::forSelection() containsObject:sendType];
1770     }
1771
1772     bool isValidReturnType = false;
1773     if (!returnType)
1774         isValidReturnType = true;
1775     else if ([PasteboardTypes::forEditing() containsObject:returnType] && editorState.isContentEditable) {
1776         // We can insert strings in any editable context.  We can insert other types, like images, only in rich edit contexts.
1777         isValidReturnType = editorState.isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType];
1778     }
1779     if (isValidSendType && isValidReturnType)
1780         return m_view;
1781     return [[m_view nextResponder] validRequestorForSendType:sendType returnType:returnType];
1782 }
1783
1784 void WebViewImpl::centerSelectionInVisibleArea()
1785 {
1786     m_page->centerSelectionInVisibleArea();
1787 }
1788
1789 void WebViewImpl::selectionDidChange()
1790 {
1791     updateFontPanelIfNeeded();
1792 }
1793
1794 void WebViewImpl::startObservingFontPanel()
1795 {
1796     [m_windowVisibilityObserver startObservingFontPanel];
1797 }
1798
1799 void WebViewImpl::updateFontPanelIfNeeded()
1800 {
1801     const EditorState& editorState = m_page->editorState();
1802     if (editorState.selectionIsNone || !editorState.isContentEditable)
1803         return;
1804     if ([NSFontPanel sharedFontPanelExists] && [[NSFontPanel sharedFontPanel] isVisible]) {
1805         m_page->fontAtSelection([](const String& fontName, double fontSize, bool selectionHasMultipleFonts, WebKit::CallbackBase::Error error) {
1806             NSFont *font = [NSFont fontWithName:fontName size:fontSize];
1807             if (font)
1808                 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:selectionHasMultipleFonts];
1809         });
1810     }
1811 }
1812
1813 void WebViewImpl::changeFontFromFontPanel()
1814 {
1815     NSFontManager *fontManager = [NSFontManager sharedFontManager];
1816     NSFont *font = [fontManager convertFont:fontManager.selectedFont];
1817     if (!font)
1818         return;
1819     m_page->setFont(font.familyName, font.pointSize, font.fontDescriptor.symbolicTraits);
1820 }
1821
1822 static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item)
1823 {
1824     if (![(NSObject *)item isKindOfClass:[NSMenuItem class]])
1825         return nil;
1826     return (NSMenuItem *)item;
1827 }
1828
1829 static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item)
1830 {
1831     if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]])
1832         return nil;
1833     return (NSToolbarItem *)item;
1834 }
1835
1836 bool WebViewImpl::validateUserInterfaceItem(id <NSValidatedUserInterfaceItem> item)
1837 {
1838     SEL action = [item action];
1839
1840     if (action == @selector(showGuessPanel:)) {
1841         if (NSMenuItem *menuItem = WebKit::menuItem(item))
1842             [menuItem setTitle:WebCore::contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])];
1843         return m_page->editorState().isContentEditable;
1844     }
1845
1846     if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:))
1847         return m_page->editorState().isContentEditable;
1848
1849     if (action == @selector(toggleContinuousSpellChecking:)) {
1850         bool enabled = TextChecker::isContinuousSpellCheckingAllowed();
1851         bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled;
1852         [menuItem(item) setState:checked ? NSOnState : NSOffState];
1853         return enabled;
1854     }
1855
1856     if (action == @selector(toggleGrammarChecking:)) {
1857         bool checked = TextChecker::state().isGrammarCheckingEnabled;
1858         [menuItem(item) setState:checked ? NSOnState : NSOffState];
1859         return true;
1860     }
1861
1862     if (action == @selector(toggleAutomaticSpellingCorrection:)) {
1863         bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled;
1864         [menuItem(item) setState:checked ? NSOnState : NSOffState];
1865         return m_page->editorState().isContentEditable;
1866     }
1867
1868     if (action == @selector(orderFrontSubstitutionsPanel:)) {
1869         if (NSMenuItem *menuItem = WebKit::menuItem(item))
1870             [menuItem setTitle:WebCore::contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])];
1871         return m_page->editorState().isContentEditable;
1872     }
1873
1874     if (action == @selector(toggleSmartInsertDelete:)) {
1875         bool checked = m_page->isSmartInsertDeleteEnabled();
1876         [menuItem(item) setState:checked ? NSOnState : NSOffState];
1877         return m_page->editorState().isContentEditable;
1878     }
1879
1880     if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
1881         bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
1882         [menuItem(item) setState:checked ? NSOnState : NSOffState];
1883         return m_page->editorState().isContentEditable;
1884     }
1885
1886     if (action == @selector(toggleAutomaticDashSubstitution:)) {
1887         bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled;
1888         [menuItem(item) setState:checked ? NSOnState : NSOffState];
1889         return m_page->editorState().isContentEditable;
1890     }
1891
1892     if (action == @selector(toggleAutomaticLinkDetection:)) {
1893         bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled;
1894         [menuItem(item) setState:checked ? NSOnState : NSOffState];
1895         return m_page->editorState().isContentEditable;
1896     }
1897
1898     if (action == @selector(toggleAutomaticTextReplacement:)) {
1899         bool checked = TextChecker::state().isAutomaticTextReplacementEnabled;
1900         [menuItem(item) setState:checked ? NSOnState : NSOffState];
1901         return m_page->editorState().isContentEditable;
1902     }
1903
1904     if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:))
1905         return m_page->editorState().selectionIsRange && m_page->editorState().isContentEditable;
1906
1907     if (action == @selector(stopSpeaking:))
1908         return [NSApp isSpeaking];
1909
1910     // The centerSelectionInVisibleArea: selector is enabled if there's a selection range or if there's an insertion point in an editable area.
1911     if (action == @selector(centerSelectionInVisibleArea:))
1912         return m_page->editorState().selectionIsRange || (m_page->editorState().isContentEditable && !m_page->editorState().selectionIsNone);
1913
1914     // Next, handle editor commands. Start by returning true for anything that is not an editor command.
1915     // Returning true is the default thing to do in an AppKit validate method for any selector that is not recognized.
1916     String commandName = commandNameForSelector([item action]);
1917     if (!WebCore::Editor::commandIsSupportedFromMenuOrKeyBinding(commandName))
1918         return true;
1919
1920     // Add this item to the vector of items for a given command that are awaiting validation.
1921     ValidationMap::AddResult addResult = m_validationMap.add(commandName, ValidationVector());
1922     addResult.iterator->value.append(item);
1923     if (addResult.isNewEntry) {
1924         // If we are not already awaiting validation for this command, start the asynchronous validation process.
1925         // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time
1926         // we asked for the same command; there is no guarantee the answer is still valid.
1927         auto weakThis = createWeakPtr();
1928         m_page->validateCommand(commandName, [weakThis](const String& commandName, bool isEnabled, int32_t state, WebKit::CallbackBase::Error error) {
1929             if (!weakThis)
1930                 return;
1931
1932             // If the process exits before the command can be validated, we'll be called back with an error.
1933             if (error != WebKit::CallbackBase::Error::None)
1934                 return;
1935
1936             weakThis->setUserInterfaceItemState(commandName, isEnabled, state);
1937         });
1938     }
1939
1940     // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called.
1941     // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment.
1942     // But returning NO here would be worse; that would make keyboard commands such as command-C fail.
1943     return true;
1944 }
1945
1946 void WebViewImpl::setUserInterfaceItemState(NSString *commandName, bool enabled, int state)
1947 {
1948     ValidationVector items = m_validationMap.take(commandName);
1949     for (auto& item : items) {
1950         [menuItem(item.get()) setState:state];
1951         [menuItem(item.get()) setEnabled:enabled];
1952         [toolbarItem(item.get()) setEnabled:enabled];
1953         // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled.
1954     }
1955 }
1956
1957 void WebViewImpl::startSpeaking()
1958 {
1959     m_page->getSelectionOrContentsAsString([](const String& string, WebKit::CallbackBase::Error error) {
1960         if (error != WebKit::CallbackBase::Error::None)
1961             return;
1962         if (!string)
1963             return;
1964
1965         [NSApp speakString:string];
1966     });
1967 }
1968
1969 void WebViewImpl::stopSpeaking(id sender)
1970 {
1971     [NSApp stopSpeaking:sender];
1972 }
1973
1974 void WebViewImpl::showGuessPanel(id sender)
1975 {
1976     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
1977     if (!checker) {
1978         LOG_ERROR("No NSSpellChecker");
1979         return;
1980     }
1981
1982     NSPanel *spellingPanel = [checker spellingPanel];
1983     if ([spellingPanel isVisible]) {
1984         [spellingPanel orderOut:sender];
1985         return;
1986     }
1987
1988     m_page->advanceToNextMisspelling(true);
1989     [spellingPanel orderFront:sender];
1990 }
1991
1992 void WebViewImpl::checkSpelling()
1993 {
1994     m_page->advanceToNextMisspelling(false);
1995 }
1996
1997 void WebViewImpl::changeSpelling(id sender)
1998 {
1999     NSString *word = [[sender selectedCell] stringValue];
2000
2001     m_page->changeSpellingToWord(word);
2002 }
2003
2004 void WebViewImpl::toggleContinuousSpellChecking()
2005 {
2006     bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled;
2007     TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled);
2008
2009     m_page->process().updateTextCheckerState();
2010 }
2011
2012 bool WebViewImpl::isGrammarCheckingEnabled()
2013 {
2014     return TextChecker::state().isGrammarCheckingEnabled;
2015 }
2016
2017 void WebViewImpl::setGrammarCheckingEnabled(bool flag)
2018 {
2019     if (static_cast<bool>(flag) == TextChecker::state().isGrammarCheckingEnabled)
2020         return;
2021
2022     TextChecker::setGrammarCheckingEnabled(flag);
2023     m_page->process().updateTextCheckerState();
2024 }
2025
2026 void WebViewImpl::toggleGrammarChecking()
2027 {
2028     bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled;
2029     TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled);
2030
2031     m_page->process().updateTextCheckerState();
2032 }
2033
2034 void WebViewImpl::toggleAutomaticSpellingCorrection()
2035 {
2036     TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled);
2037
2038     m_page->process().updateTextCheckerState();
2039 }
2040
2041 void WebViewImpl::orderFrontSubstitutionsPanel(id sender)
2042 {
2043     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2044     if (!checker) {
2045         LOG_ERROR("No NSSpellChecker");
2046         return;
2047     }
2048
2049     NSPanel *substitutionsPanel = [checker substitutionsPanel];
2050     if ([substitutionsPanel isVisible]) {
2051         [substitutionsPanel orderOut:sender];
2052         return;
2053     }
2054     [substitutionsPanel orderFront:sender];
2055 }
2056
2057 void WebViewImpl::toggleSmartInsertDelete()
2058 {
2059     m_page->setSmartInsertDeleteEnabled(!m_page->isSmartInsertDeleteEnabled());
2060 }
2061
2062 bool WebViewImpl::isAutomaticQuoteSubstitutionEnabled()
2063 {
2064     return TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
2065 }
2066
2067 void WebViewImpl::setAutomaticQuoteSubstitutionEnabled(bool flag)
2068 {
2069     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled)
2070         return;
2071
2072     TextChecker::setAutomaticQuoteSubstitutionEnabled(flag);
2073     m_page->process().updateTextCheckerState();
2074 }
2075
2076 void WebViewImpl::toggleAutomaticQuoteSubstitution()
2077 {
2078     TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled);
2079     m_page->process().updateTextCheckerState();
2080 }
2081
2082 bool WebViewImpl::isAutomaticDashSubstitutionEnabled()
2083 {
2084     return TextChecker::state().isAutomaticDashSubstitutionEnabled;
2085 }
2086
2087 void WebViewImpl::setAutomaticDashSubstitutionEnabled(bool flag)
2088 {
2089     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled)
2090         return;
2091
2092     TextChecker::setAutomaticDashSubstitutionEnabled(flag);
2093     m_page->process().updateTextCheckerState();
2094 }
2095
2096 void WebViewImpl::toggleAutomaticDashSubstitution()
2097 {
2098     TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled);
2099     m_page->process().updateTextCheckerState();
2100 }
2101
2102 bool WebViewImpl::isAutomaticLinkDetectionEnabled()
2103 {
2104     return TextChecker::state().isAutomaticLinkDetectionEnabled;
2105 }
2106
2107 void WebViewImpl::setAutomaticLinkDetectionEnabled(bool flag)
2108 {
2109     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled)
2110         return;
2111
2112     TextChecker::setAutomaticLinkDetectionEnabled(flag);
2113     m_page->process().updateTextCheckerState();
2114 }
2115
2116 void WebViewImpl::toggleAutomaticLinkDetection()
2117 {
2118     TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled);
2119     m_page->process().updateTextCheckerState();
2120 }
2121
2122 bool WebViewImpl::isAutomaticTextReplacementEnabled()
2123 {
2124     return TextChecker::state().isAutomaticTextReplacementEnabled;
2125 }
2126
2127 void WebViewImpl::setAutomaticTextReplacementEnabled(bool flag)
2128 {
2129     if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled)
2130         return;
2131     
2132     TextChecker::setAutomaticTextReplacementEnabled(flag);
2133     m_page->process().updateTextCheckerState();
2134 }
2135
2136 void WebViewImpl::toggleAutomaticTextReplacement()
2137 {
2138     TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled);
2139     m_page->process().updateTextCheckerState();
2140 }
2141
2142 void WebViewImpl::uppercaseWord()
2143 {
2144     m_page->uppercaseWord();
2145 }
2146
2147 void WebViewImpl::lowercaseWord()
2148 {
2149     m_page->lowercaseWord();
2150 }
2151
2152 void WebViewImpl::capitalizeWord()
2153 {
2154     m_page->capitalizeWord();
2155 }
2156
2157 void WebViewImpl::preferencesDidChange()
2158 {
2159     BOOL needsViewFrameInWindowCoordinates = m_page->preferences().pluginsEnabled();
2160
2161     if (!!needsViewFrameInWindowCoordinates == !!m_needsViewFrameInWindowCoordinates)
2162         return;
2163
2164     m_needsViewFrameInWindowCoordinates = needsViewFrameInWindowCoordinates;
2165     if (m_view.window)
2166         updateWindowAndViewFrames();
2167 }
2168
2169 void WebViewImpl::setTextIndicator(WebCore::TextIndicator& textIndicator, WebCore::TextIndicatorWindowLifetime lifetime)
2170 {
2171     if (!m_textIndicatorWindow)
2172         m_textIndicatorWindow = std::make_unique<WebCore::TextIndicatorWindow>(m_view);
2173
2174     NSRect textBoundingRectInScreenCoordinates = [m_view.window convertRectToScreen:[m_view convertRect:textIndicator.textBoundingRectInRootViewCoordinates() toView:nil]];
2175     m_textIndicatorWindow->setTextIndicator(textIndicator, NSRectToCGRect(textBoundingRectInScreenCoordinates), lifetime);
2176 }
2177
2178 void WebViewImpl::clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation animation)
2179 {
2180     if (m_textIndicatorWindow)
2181         m_textIndicatorWindow->clearTextIndicator(animation);
2182     m_textIndicatorWindow = nullptr;
2183 }
2184
2185 void WebViewImpl::setTextIndicatorAnimationProgress(float progress)
2186 {
2187     if (m_textIndicatorWindow)
2188         m_textIndicatorWindow->setAnimationProgress(progress);
2189 }
2190
2191 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimation(bool animate)
2192 {
2193     [m_view _web_dismissContentRelativeChildWindowsWithAnimation:animate];
2194 }
2195
2196 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimationFromViewOnly(bool animate)
2197 {
2198     // Calling _clearTextIndicatorWithAnimation here will win out over the animated clear in dismissContentRelativeChildWindowsFromViewOnly.
2199     // We can't invert these because clients can override (and have overridden) _dismissContentRelativeChildWindows, so it needs to be called.
2200     // For this same reason, this can't be moved to WebViewImpl without care.
2201     clearTextIndicatorWithAnimation(animate ? WebCore::TextIndicatorWindowDismissalAnimation::FadeOut : WebCore::TextIndicatorWindowDismissalAnimation::None);
2202     [m_view _web_dismissContentRelativeChildWindows];
2203 }
2204
2205 void WebViewImpl::dismissContentRelativeChildWindowsFromViewOnly()
2206 {
2207     bool hasActiveImmediateAction = false;
2208 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2209     hasActiveImmediateAction = [m_immediateActionController hasActiveImmediateAction];
2210 #endif
2211
2212     // FIXME: We don't know which panel we are dismissing, it may not even be in the current page (see <rdar://problem/13875766>).
2213     if (m_view.window.isKeyWindow || hasActiveImmediateAction) {
2214         WebCore::DictionaryLookup::hidePopup();
2215
2216 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2217         DDActionsManager *actionsManager = [getDDActionsManagerClass() sharedManager];
2218         if ([actionsManager respondsToSelector:@selector(requestBubbleClosureUnanchorOnFailure:)])
2219             [actionsManager requestBubbleClosureUnanchorOnFailure:YES];
2220 #endif
2221     }
2222
2223     clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::FadeOut);
2224
2225 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2226     [m_immediateActionController dismissContentRelativeChildWindows];
2227 #endif
2228     
2229     m_pageClient->dismissCorrectionPanel(WebCore::ReasonForDismissingAlternativeTextIgnored);
2230 }
2231
2232 void WebViewImpl::hideWordDefinitionWindow()
2233 {
2234     WebCore::DictionaryLookup::hidePopup();
2235 }
2236
2237 void WebViewImpl::quickLookWithEvent(NSEvent *event)
2238 {
2239     if (ignoresNonWheelEvents())
2240         return;
2241
2242 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2243     if (m_immediateActionGestureRecognizer) {
2244         [m_view _web_superQuickLookWithEvent:event];
2245         return;
2246     }
2247 #endif
2248
2249     NSPoint locationInViewCoordinates = [m_view convertPoint:[event locationInWindow] fromView:nil];
2250     m_page->performDictionaryLookupAtLocation(WebCore::FloatPoint(locationInViewCoordinates));
2251 }
2252
2253 void WebViewImpl::prepareForDictionaryLookup()
2254 {
2255     if (m_didRegisterForLookupPopoverCloseNotifications)
2256         return;
2257
2258     m_didRegisterForLookupPopoverCloseNotifications = true;
2259
2260     [m_windowVisibilityObserver startObservingLookupDismissal];
2261 }
2262
2263 void WebViewImpl::setAllowsLinkPreview(bool allowsLinkPreview)
2264 {
2265     if (m_allowsLinkPreview == allowsLinkPreview)
2266         return;
2267
2268     m_allowsLinkPreview = allowsLinkPreview;
2269
2270 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2271     if (!allowsLinkPreview)
2272         [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
2273     else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get())
2274         [m_view addGestureRecognizer:immediateActionRecognizer];
2275 #endif
2276 }
2277
2278 void* WebViewImpl::immediateActionAnimationControllerForHitTestResult(API::HitTestResult* hitTestResult, uint32_t type, API::Object* userData)
2279 {
2280     return [m_view _web_immediateActionAnimationControllerForHitTestResultInternal:hitTestResult withType:type userData:userData];
2281 }
2282
2283 void WebViewImpl::didPerformImmediateActionHitTest(const WebHitTestResultData& result, bool contentPreventsDefault, API::Object* userData)
2284 {
2285 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2286     [m_immediateActionController didPerformImmediateActionHitTest:result contentPreventsDefault:contentPreventsDefault userData:userData];
2287 #endif
2288 }
2289
2290 void WebViewImpl::prepareForImmediateActionAnimation()
2291 {
2292     [m_view _web_prepareForImmediateActionAnimation];
2293 }
2294
2295 void WebViewImpl::cancelImmediateActionAnimation()
2296 {
2297     [m_view _web_cancelImmediateActionAnimation];
2298 }
2299
2300 void WebViewImpl::completeImmediateActionAnimation()
2301 {
2302     [m_view _web_completeImmediateActionAnimation];
2303 }
2304
2305 void WebViewImpl::didChangeContentSize(CGSize newSize)
2306 {
2307     [m_view _web_didChangeContentSize:NSSizeFromCGSize(newSize)];
2308 }
2309
2310 void WebViewImpl::setIgnoresNonWheelEvents(bool ignoresNonWheelEvents)
2311 {
2312     if (m_ignoresNonWheelEvents == ignoresNonWheelEvents)
2313         return;
2314
2315     m_ignoresNonWheelEvents = ignoresNonWheelEvents;
2316     m_page->setShouldDispatchFakeMouseMoveEvents(!ignoresNonWheelEvents);
2317
2318 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2319     if (ignoresNonWheelEvents)
2320         [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
2321     else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get()) {
2322         if (m_allowsLinkPreview)
2323             [m_view addGestureRecognizer:immediateActionRecognizer];
2324     }
2325 #endif
2326 }
2327
2328 void WebViewImpl::setIgnoresAllEvents(bool ignoresAllEvents)
2329 {
2330     m_ignoresAllEvents = ignoresAllEvents;
2331     setIgnoresNonWheelEvents(ignoresAllEvents);
2332 }
2333
2334 void WebViewImpl::setIgnoresMouseDraggedEvents(bool ignoresMouseDraggedEvents)
2335 {
2336     m_ignoresMouseDraggedEvents = ignoresMouseDraggedEvents;
2337 }
2338
2339 void WebViewImpl::setAccessibilityWebProcessToken(NSData *data)
2340 {
2341     m_remoteAccessibilityChild = WKAXRemoteElementForToken(data);
2342     updateRemoteAccessibilityRegistration(true);
2343 }
2344
2345 void WebViewImpl::updateRemoteAccessibilityRegistration(bool registerProcess)
2346 {
2347     // When the tree is connected/disconnected, the remote accessibility registration
2348     // needs to be updated with the pid of the remote process. If the process is going
2349     // away, that information is not present in WebProcess
2350     pid_t pid = 0;
2351     if (registerProcess)
2352         pid = m_page->process().processIdentifier();
2353     else if (!registerProcess) {
2354         pid = WKAXRemoteProcessIdentifier(m_remoteAccessibilityChild.get());
2355         m_remoteAccessibilityChild = nil;
2356     }
2357     if (pid)
2358         WKAXRegisterRemoteProcess(registerProcess, pid);
2359 }
2360
2361 void WebViewImpl::accessibilityRegisterUIProcessTokens()
2362 {
2363     // Initialize remote accessibility when the window connection has been established.
2364     NSData *remoteElementToken = WKAXRemoteTokenForElement(m_view);
2365     NSData *remoteWindowToken = WKAXRemoteTokenForElement(m_view.window);
2366     IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
2367     IPC::DataReference windowToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]);
2368     m_page->registerUIProcessAccessibilityTokens(elementToken, windowToken);
2369 }
2370
2371 id WebViewImpl::accessibilityFocusedUIElement()
2372 {
2373     enableAccessibilityIfNecessary();
2374     return m_remoteAccessibilityChild.get();
2375 }
2376
2377 id WebViewImpl::accessibilityHitTest(CGPoint)
2378 {
2379     return accessibilityFocusedUIElement();
2380 }
2381
2382 void WebViewImpl::enableAccessibilityIfNecessary()
2383 {
2384     if (WebCore::AXObjectCache::accessibilityEnabled())
2385         return;
2386
2387     // After enabling accessibility update the window frame on the web process so that the
2388     // correct accessibility position is transmitted (when AX is off, that position is not calculated).
2389     WebCore::AXObjectCache::enableAccessibility();
2390     updateWindowAndViewFrames();
2391 }
2392
2393 id WebViewImpl::accessibilityAttributeValue(NSString *attribute)
2394 {
2395     enableAccessibilityIfNecessary();
2396
2397     if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
2398
2399         id child = nil;
2400         if (m_remoteAccessibilityChild)
2401             child = m_remoteAccessibilityChild.get();
2402
2403             if (!child)
2404                 return nil;
2405         return [NSArray arrayWithObject:child];
2406     }
2407     if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
2408         return NSAccessibilityGroupRole;
2409     if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
2410         return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
2411         if ([attribute isEqualToString:NSAccessibilityParentAttribute])
2412             return NSAccessibilityUnignoredAncestor([m_view superview]);
2413             if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
2414                 return @YES;
2415     
2416     return [m_view _web_superAccessibilityAttributeValue:attribute];
2417 }
2418
2419 void WebViewImpl::setPrimaryTrackingArea(NSTrackingArea *trackingArea)
2420 {
2421     [m_view removeTrackingArea:m_primaryTrackingArea.get()];
2422     m_primaryTrackingArea = trackingArea;
2423     [m_view addTrackingArea:trackingArea];
2424 }
2425
2426 // Any non-zero value will do, but using something recognizable might help us debug some day.
2427 #define TRACKING_RECT_TAG 0xBADFACE
2428
2429 NSTrackingRectTag WebViewImpl::addTrackingRect(CGRect, id owner, void* userData, bool assumeInside)
2430 {
2431     ASSERT(m_trackingRectOwner == nil);
2432     m_trackingRectOwner = owner;
2433     m_trackingRectUserData = userData;
2434     return TRACKING_RECT_TAG;
2435 }
2436
2437 NSTrackingRectTag WebViewImpl::addTrackingRectWithTrackingNum(CGRect, id owner, void* userData, bool assumeInside, int tag)
2438 {
2439     ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
2440     ASSERT(m_trackingRectOwner == nil);
2441     m_trackingRectOwner = owner;
2442     m_trackingRectUserData = userData;
2443     return TRACKING_RECT_TAG;
2444 }
2445
2446 void WebViewImpl::addTrackingRectsWithTrackingNums(CGRect*, id owner, void** userDataList, bool assumeInside, NSTrackingRectTag *trackingNums, int count)
2447 {
2448     ASSERT(count == 1);
2449     ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
2450     ASSERT(m_trackingRectOwner == nil);
2451     m_trackingRectOwner = owner;
2452     m_trackingRectUserData = userDataList[0];
2453     trackingNums[0] = TRACKING_RECT_TAG;
2454 }
2455
2456 void WebViewImpl::removeTrackingRect(NSTrackingRectTag tag)
2457 {
2458     if (tag == 0)
2459         return;
2460
2461     if (tag == TRACKING_RECT_TAG) {
2462         m_trackingRectOwner = nil;
2463         return;
2464     }
2465
2466     if (tag == m_lastToolTipTag) {
2467         [m_view _web_superRemoveTrackingRect:tag];
2468         m_lastToolTipTag = 0;
2469         return;
2470     }
2471
2472     // If any other tracking rect is being removed, we don't know how it was created
2473     // and it's possible there's a leak involved (see 3500217)
2474     ASSERT_NOT_REACHED();
2475 }
2476
2477 void WebViewImpl::removeTrackingRects(NSTrackingRectTag *tags, int count)
2478 {
2479     for (int i = 0; i < count; ++i) {
2480         int tag = tags[i];
2481         if (tag == 0)
2482             continue;
2483         ASSERT(tag == TRACKING_RECT_TAG);
2484         m_trackingRectOwner = nil;
2485     }
2486 }
2487
2488 void WebViewImpl::sendToolTipMouseExited()
2489 {
2490     // Nothing matters except window, trackingNumber, and userData.
2491     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
2492                                                 location:NSMakePoint(0, 0)
2493                                            modifierFlags:0
2494                                                timestamp:0
2495                                             windowNumber:m_view.window.windowNumber
2496                                                  context:NULL
2497                                              eventNumber:0
2498                                           trackingNumber:TRACKING_RECT_TAG
2499                                                 userData:m_trackingRectUserData];
2500     [m_trackingRectOwner mouseExited:fakeEvent];
2501 }
2502
2503 void WebViewImpl::sendToolTipMouseEntered()
2504 {
2505     // Nothing matters except window, trackingNumber, and userData.
2506     NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
2507                                                 location:NSMakePoint(0, 0)
2508                                            modifierFlags:0
2509                                                timestamp:0
2510                                             windowNumber:m_view.window.windowNumber
2511                                                  context:NULL
2512                                              eventNumber:0
2513                                           trackingNumber:TRACKING_RECT_TAG
2514                                                 userData:m_trackingRectUserData];
2515     [m_trackingRectOwner mouseEntered:fakeEvent];
2516 }
2517
2518 NSString *WebViewImpl::stringForToolTip(NSToolTipTag tag)
2519 {
2520     return nsStringFromWebCoreString(m_page->toolTip());
2521 }
2522
2523 void WebViewImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip)
2524 {
2525     if (!oldToolTip.isNull())
2526         sendToolTipMouseExited();
2527     
2528     if (!newToolTip.isEmpty()) {
2529         // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
2530         [m_view removeAllToolTips];
2531         NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
2532         m_lastToolTipTag = [m_view addToolTipRect:wideOpenRect owner:m_view userData:NULL];
2533         sendToolTipMouseEntered();
2534     }
2535 }
2536
2537 void WebViewImpl::setAcceleratedCompositingRootLayer(CALayer *rootLayer)
2538 {
2539     [rootLayer web_disableAllActions];
2540
2541     m_rootLayer = rootLayer;
2542
2543 #if WK_API_ENABLED
2544     if (m_thumbnailView) {
2545         updateThumbnailViewLayer();
2546         return;
2547     }
2548 #endif
2549
2550     [CATransaction begin];
2551     [CATransaction setDisableActions:YES];
2552
2553     if (rootLayer) {
2554         if (!m_layerHostingView) {
2555             // Create an NSView that will host our layer tree.
2556             m_layerHostingView = adoptNS([[WKFlippedView alloc] initWithFrame:m_view.bounds]);
2557             [m_layerHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
2558
2559             [m_view addSubview:m_layerHostingView.get() positioned:NSWindowBelow relativeTo:nil];
2560
2561             // Create a root layer that will back the NSView.
2562             RetainPtr<CALayer> layer = adoptNS([[CALayer alloc] init]);
2563             [layer setDelegate:[WebActionDisablingCALayerDelegate shared]];
2564 #ifndef NDEBUG
2565             [layer setName:@"Hosting root layer"];
2566 #endif
2567
2568             [m_layerHostingView setLayer:layer.get()];
2569             [m_layerHostingView setWantsLayer:YES];
2570         }
2571
2572         [m_layerHostingView layer].sublayers = [NSArray arrayWithObject:rootLayer];
2573     } else if (m_layerHostingView) {
2574         [m_layerHostingView removeFromSuperview];
2575         [m_layerHostingView setLayer:nil];
2576         [m_layerHostingView setWantsLayer:NO];
2577
2578         m_layerHostingView = nullptr;
2579     }
2580
2581     [CATransaction commit];
2582 }
2583
2584 #if WK_API_ENABLED
2585 void WebViewImpl::setThumbnailView(_WKThumbnailView *thumbnailView)
2586 {
2587     ASSERT(!m_thumbnailView || !thumbnailView);
2588
2589     m_thumbnailView = thumbnailView;
2590
2591     if (thumbnailView)
2592         updateThumbnailViewLayer();
2593     else
2594         setAcceleratedCompositingRootLayer(m_rootLayer.get());
2595 }
2596
2597 void WebViewImpl::reparentLayerTreeInThumbnailView()
2598 {
2599     m_thumbnailView._thumbnailLayer = m_rootLayer.get();
2600 }
2601
2602 void WebViewImpl::updateThumbnailViewLayer()
2603 {
2604     _WKThumbnailView *thumbnailView = m_thumbnailView;
2605     ASSERT(thumbnailView);
2606
2607     if (thumbnailView._waitingForSnapshot && m_view.window)
2608         reparentLayerTreeInThumbnailView();
2609 }
2610
2611 void WebViewImpl::setInspectorAttachmentView(NSView *newView)
2612 {
2613     NSView *oldView = m_inspectorAttachmentView.get();
2614     if (oldView == newView)
2615         return;
2616
2617     m_inspectorAttachmentView = newView;
2618     m_page->inspector()->attachmentViewDidChange(oldView ? oldView : m_view, newView ? newView : m_view);
2619 }
2620
2621 NSView *WebViewImpl::inspectorAttachmentView()
2622 {
2623     NSView *attachmentView = m_inspectorAttachmentView.get();
2624     return attachmentView ? attachmentView : m_view;
2625 }
2626
2627 _WKRemoteObjectRegistry *WebViewImpl::remoteObjectRegistry()
2628 {
2629     if (!m_remoteObjectRegistry) {
2630         m_remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:m_page]);
2631         m_page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID(), [m_remoteObjectRegistry remoteObjectRegistry]);
2632     }
2633
2634     return m_remoteObjectRegistry.get();
2635 }
2636
2637 WKBrowsingContextController *WebViewImpl::browsingContextController()
2638 {
2639     if (!m_browsingContextController)
2640         m_browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(m_page.ptr())]);
2641
2642     return m_browsingContextController.get();
2643 }
2644 #endif // WK_API_ENABLED
2645
2646 #if ENABLE(DRAG_SUPPORT)
2647 void WebViewImpl::draggedImage(NSImage *image, CGPoint endPoint, NSDragOperation operation)
2648 {
2649 #pragma clang diagnostic push
2650 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2651     NSPoint windowImageLoc = [m_view.window convertScreenToBase:NSPointFromCGPoint(endPoint)];
2652 #pragma clang diagnostic pop
2653     NSPoint windowMouseLoc = windowImageLoc;
2654
2655     // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
2656     m_ignoresMouseDraggedEvents = true;
2657
2658     m_page->dragEnded(WebCore::IntPoint(windowMouseLoc), WebCore::globalPoint(windowMouseLoc, m_view.window), operation);
2659 }
2660
2661 static WebCore::DragApplicationFlags applicationFlagsForDrag(NSView *view, id <NSDraggingInfo> draggingInfo)
2662 {
2663     uint32_t flags = 0;
2664     if ([NSApp modalWindow])
2665         flags = WebCore::DragApplicationIsModal;
2666     if (view.window.attachedSheet)
2667         flags |= WebCore::DragApplicationHasAttachedSheet;
2668     if (draggingInfo.draggingSource == view)
2669         flags |= WebCore::DragApplicationIsSource;
2670     if ([NSApp currentEvent].modifierFlags & NSAlternateKeyMask)
2671         flags |= WebCore::DragApplicationIsCopyKeyDown;
2672     return static_cast<WebCore::DragApplicationFlags>(flags);
2673
2674 }
2675
2676 NSDragOperation WebViewImpl::draggingEntered(id <NSDraggingInfo> draggingInfo)
2677 {
2678     WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2679     WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2680     WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2681
2682     m_page->resetCurrentDragInformation();
2683     m_page->dragEntered(dragData, draggingInfo.draggingPasteboard.name);
2684     return NSDragOperationCopy;
2685 }
2686
2687 NSDragOperation WebViewImpl::draggingUpdated(id <NSDraggingInfo> draggingInfo)
2688 {
2689     WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2690     WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2691     WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2692     m_page->dragUpdated(dragData, draggingInfo.draggingPasteboard.name);
2693
2694     NSInteger numberOfValidItemsForDrop = m_page->currentDragNumberOfFilesToBeAccepted();
2695     NSDraggingFormation draggingFormation = NSDraggingFormationNone;
2696     if (m_page->currentDragIsOverFileInput() && numberOfValidItemsForDrop > 0)
2697         draggingFormation = NSDraggingFormationList;
2698
2699     if (draggingInfo.numberOfValidItemsForDrop != numberOfValidItemsForDrop)
2700         [draggingInfo setNumberOfValidItemsForDrop:numberOfValidItemsForDrop];
2701     if (draggingInfo.draggingFormation != draggingFormation)
2702         [draggingInfo setDraggingFormation:draggingFormation];
2703
2704     return m_page->currentDragOperation();
2705 }
2706
2707 void WebViewImpl::draggingExited(id <NSDraggingInfo> draggingInfo)
2708 {
2709     WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2710     WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2711     WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2712     m_page->dragExited(dragData, draggingInfo.draggingPasteboard.name);
2713     m_page->resetCurrentDragInformation();
2714 }
2715
2716 bool WebViewImpl::prepareForDragOperation(id <NSDraggingInfo>)
2717 {
2718     return true;
2719 }
2720
2721 // FIXME: This code is more or less copied from Pasteboard::getBestURL.
2722 // It would be nice to be able to share the code somehow.
2723 static bool maybeCreateSandboxExtensionFromPasteboard(NSPasteboard *pasteboard, SandboxExtension::Handle& sandboxExtensionHandle)
2724 {
2725     NSArray *types = pasteboard.types;
2726     if (![types containsObject:NSFilenamesPboardType])
2727         return false;
2728
2729     NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
2730     if (files.count != 1)
2731         return false;
2732
2733     NSString *file = [files objectAtIndex:0];
2734     BOOL isDirectory;
2735     if (![[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory])
2736         return false;
2737
2738     if (isDirectory)
2739         return false;
2740
2741     SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle);
2742     return true;
2743 }
2744
2745 static void createSandboxExtensionsForFileUpload(NSPasteboard *pasteboard, SandboxExtension::HandleArray& handles)
2746 {
2747     NSArray *types = pasteboard.types;
2748     if (![types containsObject:NSFilenamesPboardType])
2749         return;
2750
2751     NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
2752     handles.allocate(files.count);
2753     for (unsigned i = 0; i < files.count; i++) {
2754         NSString *file = [files objectAtIndex:i];
2755         if (![[NSFileManager defaultManager] fileExistsAtPath:file])
2756             continue;
2757         SandboxExtension::Handle handle;
2758         SandboxExtension::createHandle(file, SandboxExtension::ReadOnly, handles[i]);
2759     }
2760 }
2761
2762 bool WebViewImpl::performDragOperation(id <NSDraggingInfo> draggingInfo)
2763 {
2764     WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2765     WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2766     WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2767
2768     SandboxExtension::Handle sandboxExtensionHandle;
2769     bool createdExtension = maybeCreateSandboxExtensionFromPasteboard(draggingInfo.draggingPasteboard, sandboxExtensionHandle);
2770     if (createdExtension)
2771         m_page->process().willAcquireUniversalFileReadSandboxExtension();
2772
2773     SandboxExtension::HandleArray sandboxExtensionForUpload;
2774     createSandboxExtensionsForFileUpload(draggingInfo.draggingPasteboard, sandboxExtensionForUpload);
2775
2776     m_page->performDragOperation(dragData, draggingInfo.draggingPasteboard.name, sandboxExtensionHandle, sandboxExtensionForUpload);
2777
2778     return true;
2779 }
2780
2781 NSView *WebViewImpl::hitTestForDragTypes(CGPoint point, NSSet *types)
2782 {
2783     // This code is needed to support drag and drop when the drag types cannot be matched.
2784     // This is the case for elements that do not place content
2785     // in the drag pasteboard automatically when the drag start (i.e. dragging a DIV element).
2786     if ([[m_view superview] mouse:NSPointFromCGPoint(point) inRect:[m_view frame]])
2787         return m_view;
2788     return nil;
2789 }
2790
2791 void WebViewImpl::registerDraggedTypes()
2792 {
2793     auto types = adoptNS([[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()]);
2794     [types addObjectsFromArray:PasteboardTypes::forURL()];
2795     [m_view registerForDraggedTypes:[types allObjects]];
2796 }
2797 #endif // ENABLE(DRAG_SUPPORT)
2798
2799 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
2800 void WebViewImpl::startWindowDrag()
2801 {
2802     [m_view.window performWindowDragWithEvent:m_lastMouseDownEvent.get()];
2803 }
2804 #endif
2805
2806 void WebViewImpl::dragImageForView(NSView *view, NSImage *image, CGPoint clientPoint, bool linkDrag)
2807 {
2808     // The call below could release the view.
2809     RetainPtr<NSView> protector(m_view);
2810
2811 #pragma clang diagnostic push
2812 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2813     [view dragImage:image
2814                  at:NSPointFromCGPoint(clientPoint)
2815              offset:NSZeroSize
2816               event:linkDrag ? [NSApp currentEvent] : m_lastMouseDownEvent.get()
2817          pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
2818              source:m_view
2819           slideBack:YES];
2820 #pragma clang diagnostic pop
2821 }
2822
2823 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
2824 {
2825     NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
2826     return hasCaseInsensitiveSuffix(filename, extensionAsSuffix) || (stringIsCaseInsensitiveEqualToString(extension, @"jpeg")
2827         && hasCaseInsensitiveSuffix(filename, @".jpg"));
2828 }
2829
2830 void WebViewImpl::setFileAndURLTypes(NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, NSPasteboard *pasteboard)
2831 {
2832     if (!matchesExtensionOrEquivalent(filename, extension))
2833         filename = [[filename stringByAppendingString:@"."] stringByAppendingString:extension];
2834
2835     [pasteboard setString:visibleURL forType:NSStringPboardType];
2836     [pasteboard setString:visibleURL forType:PasteboardTypes::WebURLPboardType];
2837     [pasteboard setString:title forType:PasteboardTypes::WebURLNamePboardType];
2838     [pasteboard setPropertyList:[NSArray arrayWithObjects:[NSArray arrayWithObject:visibleURL], [NSArray arrayWithObject:title], nil] forType:PasteboardTypes::WebURLsWithTitlesPboardType];
2839     [pasteboard setPropertyList:[NSArray arrayWithObject:extension] forType:NSFilesPromisePboardType];
2840     m_promisedFilename = filename;
2841     m_promisedURL = url;
2842 }
2843
2844 void WebViewImpl::setPromisedDataForImage(WebCore::Image* image, NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, WebCore::SharedBuffer* archiveBuffer, NSString *pasteboardName)
2845 {
2846     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
2847     RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
2848
2849     [types addObjectsFromArray:archiveBuffer ? PasteboardTypes::forImagesWithArchive() : PasteboardTypes::forImages()];
2850     [pasteboard declareTypes:types.get() owner:m_view];
2851     setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
2852
2853     if (archiveBuffer)
2854         [pasteboard setData:archiveBuffer->createNSData().get() forType:PasteboardTypes::WebArchivePboardType];
2855
2856     m_promisedImage = image;
2857 }
2858
2859 #if ENABLE(ATTACHMENT_ELEMENT)
2860 void WebViewImpl::setPromisedDataForAttachment(NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, NSString *pasteboardName)
2861 {
2862     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
2863     RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
2864     [types addObjectsFromArray:PasteboardTypes::forURL()];
2865     [pasteboard declareTypes:types.get() owner:m_view];
2866     setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
2867     
2868     RetainPtr<NSMutableArray> paths = adoptNS([[NSMutableArray alloc] init]);
2869     [paths addObject:title];
2870     [pasteboard setPropertyList:paths.get() forType:NSFilenamesPboardType];
2871     
2872     m_promisedImage = nullptr;
2873 }
2874 #endif
2875
2876 void WebViewImpl::pasteboardChangedOwner(NSPasteboard *pasteboard)
2877 {
2878     m_promisedImage = nullptr;
2879     m_promisedFilename = "";
2880     m_promisedURL = "";
2881 }
2882
2883 void WebViewImpl::provideDataForPasteboard(NSPasteboard *pasteboard, NSString *type)
2884 {
2885     // FIXME: need to support NSRTFDPboardType
2886
2887     if ([type isEqual:NSTIFFPboardType] && m_promisedImage) {
2888         [pasteboard setData:(NSData *)m_promisedImage->getTIFFRepresentation() forType:NSTIFFPboardType];
2889         m_promisedImage = nullptr;
2890     }
2891 }
2892
2893 static BOOL fileExists(NSString *path)
2894 {
2895     struct stat statBuffer;
2896     return !lstat([path fileSystemRepresentation], &statBuffer);
2897 }
2898
2899 static NSString *pathWithUniqueFilenameForPath(NSString *path)
2900 {
2901     // "Fix" the filename of the path.
2902     NSString *filename = filenameByFixingIllegalCharacters([path lastPathComponent]);
2903     path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename];
2904     
2905     if (fileExists(path)) {
2906         // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename.
2907         NSString *extensions = nil;
2908         NSString *pathWithoutExtensions;
2909         NSString *lastPathComponent = [path lastPathComponent];
2910         NSRange periodRange = [lastPathComponent rangeOfString:@"."];
2911         
2912         if (periodRange.location == NSNotFound) {
2913             pathWithoutExtensions = path;
2914         } else {
2915             extensions = [lastPathComponent substringFromIndex:periodRange.location + 1];
2916             lastPathComponent = [lastPathComponent substringToIndex:periodRange.location];
2917             pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent];
2918         }
2919         
2920         for (unsigned i = 1; ; i++) {
2921             NSString *pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i];
2922             path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber;
2923             if (!fileExists(path))
2924                 break;
2925         }
2926     }
2927     
2928     return path;
2929 }
2930
2931 NSArray *WebViewImpl::namesOfPromisedFilesDroppedAtDestination(NSURL *dropDestination)
2932 {
2933     RetainPtr<NSFileWrapper> wrapper;
2934     RetainPtr<NSData> data;
2935     
2936     if (m_promisedImage) {
2937         data = m_promisedImage->data()->createNSData();
2938         wrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data.get()]);
2939     } else
2940         wrapper = adoptNS([[NSFileWrapper alloc] initWithURL:[NSURL URLWithString:m_promisedURL] options:NSFileWrapperReadingImmediate error:nil]);
2941     
2942     if (wrapper)
2943         [wrapper setPreferredFilename:m_promisedFilename];
2944     else {
2945         LOG_ERROR("Failed to create image file.");
2946         return nil;
2947     }
2948     
2949     // FIXME: Report an error if we fail to create a file.
2950     NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
2951     path = pathWithUniqueFilenameForPath(path);
2952     if (![wrapper writeToURL:[NSURL fileURLWithPath:path] options:NSFileWrapperWritingWithNameUpdating originalContentsURL:nil error:nullptr])
2953         LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToURL:options:originalContentsURL:error:]");
2954
2955     if (!m_promisedURL.isEmpty())
2956         WebCore::setMetadataURL(m_promisedURL, "", String(path));
2957     
2958     return [NSArray arrayWithObject:[path lastPathComponent]];
2959 }
2960
2961 static RetainPtr<CGImageRef> takeWindowSnapshot(CGSWindowID windowID, bool captureAtNominalResolution)
2962 {
2963     CGSWindowCaptureOptions options = kCGSCaptureIgnoreGlobalClipShape;
2964     if (captureAtNominalResolution)
2965         options |= kCGSWindowCaptureNominalResolution;
2966     RetainPtr<CFArrayRef> windowSnapshotImages = adoptCF(CGSHWCaptureWindowList(CGSMainConnectionID(), &windowID, 1, options));
2967
2968     if (windowSnapshotImages && CFArrayGetCount(windowSnapshotImages.get()))
2969         return (CGImageRef)CFArrayGetValueAtIndex(windowSnapshotImages.get(), 0);
2970
2971     // Fall back to the non-hardware capture path if we didn't get a snapshot
2972     // (which usually happens if the window is fully off-screen).
2973     CGWindowImageOption imageOptions = kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque;
2974     if (captureAtNominalResolution)
2975         imageOptions |= kCGWindowImageNominalResolution;
2976     return adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, imageOptions));
2977 }
2978
2979 RefPtr<ViewSnapshot> WebViewImpl::takeViewSnapshot()
2980 {
2981     NSWindow *window = m_view.window;
2982
2983     CGSWindowID windowID = (CGSWindowID)window.windowNumber;
2984     if (!windowID || !window.isVisible)
2985         return nullptr;
2986
2987     RetainPtr<CGImageRef> windowSnapshotImage = takeWindowSnapshot(windowID, false);
2988     if (!windowSnapshotImage)
2989         return nullptr;
2990
2991     // Work around <rdar://problem/17084993>; re-request the snapshot at kCGWindowImageNominalResolution if it was captured at the wrong scale.
2992     CGFloat desiredSnapshotWidth = window.frame.size.width * window.screen.backingScaleFactor;
2993     if (CGImageGetWidth(windowSnapshotImage.get()) != desiredSnapshotWidth)
2994         windowSnapshotImage = takeWindowSnapshot(windowID, true);
2995
2996     if (!windowSnapshotImage)
2997         return nullptr;
2998
2999     ViewGestureController& gestureController = ensureGestureController();
3000
3001     NSRect windowCaptureRect;
3002     WebCore::FloatRect boundsForCustomSwipeViews = gestureController.windowRelativeBoundsForCustomSwipeViews();
3003     if (!boundsForCustomSwipeViews.isEmpty())
3004         windowCaptureRect = boundsForCustomSwipeViews;
3005     else {
3006         NSRect unobscuredBounds = m_view.bounds;
3007         float topContentInset = m_page->topContentInset();
3008         unobscuredBounds.origin.y += topContentInset;
3009         unobscuredBounds.size.height -= topContentInset;
3010         windowCaptureRect = [m_view convertRect:unobscuredBounds toView:nil];
3011     }
3012
3013     NSRect windowCaptureScreenRect = [window convertRectToScreen:windowCaptureRect];
3014     CGRect windowScreenRect;
3015     CGSGetScreenRectForWindow(CGSMainConnectionID(), (CGSWindowID)[window windowNumber], &windowScreenRect);
3016
3017     NSRect croppedImageRect = windowCaptureRect;
3018     croppedImageRect.origin.y = windowScreenRect.size.height - windowCaptureScreenRect.size.height - NSMinY(windowCaptureRect);
3019
3020     auto croppedSnapshotImage = adoptCF(CGImageCreateWithImageInRect(windowSnapshotImage.get(), NSRectToCGRect([window convertRectToBacking:croppedImageRect])));
3021
3022     auto surface = WebCore::IOSurface::createFromImage(croppedSnapshotImage.get());
3023     if (!surface)
3024         return nullptr;
3025     surface->setIsVolatile(true);
3026
3027     return ViewSnapshot::create(WTF::move(surface));
3028 }
3029
3030 void WebViewImpl::saveBackForwardSnapshotForCurrentItem()
3031 {
3032     m_page->recordNavigationSnapshot();
3033 }
3034
3035 void WebViewImpl::saveBackForwardSnapshotForItem(WebBackForwardListItem& item)
3036 {
3037     m_page->recordNavigationSnapshot(item);
3038 }
3039
3040 ViewGestureController& WebViewImpl::ensureGestureController()
3041 {
3042     if (!m_gestureController)
3043         m_gestureController = std::make_unique<ViewGestureController>(m_page);
3044     return *m_gestureController;
3045 }
3046
3047 void WebViewImpl::setAllowsBackForwardNavigationGestures(bool allowsBackForwardNavigationGestures)
3048 {
3049     m_allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
3050     m_page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
3051     m_page->setShouldUseImplicitRubberBandControl(allowsBackForwardNavigationGestures);
3052 }
3053
3054 void WebViewImpl::setAllowsMagnification(bool allowsMagnification)
3055 {
3056     m_allowsMagnification = allowsMagnification;
3057 }
3058
3059 void WebViewImpl::setMagnification(double magnification, CGPoint centerPoint)
3060 {
3061     if (magnification <= 0 || isnan(magnification) || isinf(magnification))
3062         [NSException raise:NSInvalidArgumentException format:@"Magnification should be a positive number"];
3063
3064     dismissContentRelativeChildWindowsWithAnimation(false);
3065
3066     m_page->scalePageInViewCoordinates(magnification, WebCore::roundedIntPoint(centerPoint));
3067 }
3068
3069 void WebViewImpl::setMagnification(double magnification)
3070 {
3071     if (magnification <= 0 || isnan(magnification) || isinf(magnification))
3072         [NSException raise:NSInvalidArgumentException format:@"Magnification should be a positive number"];
3073
3074     dismissContentRelativeChildWindowsWithAnimation(false);
3075
3076     WebCore::FloatPoint viewCenter(NSMidX([m_view bounds]), NSMidY([m_view bounds]));
3077     m_page->scalePageInViewCoordinates(magnification, roundedIntPoint(viewCenter));
3078 }
3079
3080 double WebViewImpl::magnification() const
3081 {
3082     if (m_gestureController)
3083         return m_gestureController->magnification();
3084     return m_page->pageScaleFactor();
3085 }
3086
3087 void WebViewImpl::setCustomSwipeViews(NSArray *customSwipeViews)
3088 {
3089     if (!customSwipeViews.count && !m_gestureController)
3090         return;
3091
3092     Vector<RetainPtr<NSView>> views;
3093     views.reserveInitialCapacity(customSwipeViews.count);
3094     for (NSView *view in customSwipeViews)
3095         views.uncheckedAppend(view);
3096
3097     ensureGestureController().setCustomSwipeViews(views);
3098 }
3099
3100 void WebViewImpl::setCustomSwipeViewsTopContentInset(float topContentInset)
3101 {
3102     ensureGestureController().setCustomSwipeViewsTopContentInset(topContentInset);
3103 }
3104
3105 bool WebViewImpl::tryToSwipeWithEvent(NSEvent *event, bool ignoringPinnedState)
3106 {
3107     if (!m_allowsBackForwardNavigationGestures)
3108         return false;
3109
3110     auto& gestureController = ensureGestureController();
3111
3112     bool wasIgnoringPinnedState = gestureController.shouldIgnorePinnedState();
3113     gestureController.setShouldIgnorePinnedState(ignoringPinnedState);
3114
3115     bool handledEvent = gestureController.handleScrollWheelEvent(event);
3116
3117     gestureController.setShouldIgnorePinnedState(wasIgnoringPinnedState);
3118     
3119     return handledEvent;
3120 }
3121
3122 void WebViewImpl::setDidMoveSwipeSnapshotCallback(void(^callback)(CGRect))
3123 {
3124     if (!m_allowsBackForwardNavigationGestures)
3125         return;
3126
3127     ensureGestureController().setDidMoveSwipeSnapshotCallback(callback);
3128 }
3129
3130 void WebViewImpl::scrollWheel(NSEvent *event)
3131 {
3132     if (m_ignoresAllEvents)
3133         return;
3134
3135     if (event.phase == NSEventPhaseBegan)
3136         dismissContentRelativeChildWindowsWithAnimation(false);
3137
3138     if (m_allowsBackForwardNavigationGestures && ensureGestureController().handleScrollWheelEvent(event))
3139         return;
3140
3141     NativeWebWheelEvent webEvent = NativeWebWheelEvent(event, m_view);
3142     m_page->handleWheelEvent(webEvent);
3143 }
3144
3145 void WebViewImpl::swipeWithEvent(NSEvent *event)
3146 {
3147     if (m_ignoresNonWheelEvents)
3148         return;
3149
3150     if (!m_allowsBackForwardNavigationGestures) {
3151         [m_view _web_superSwipeWithEvent:event];
3152         return;
3153     }
3154
3155     if (event.deltaX > 0.0)
3156         m_page->goBack();
3157     else if (event.deltaX < 0.0)
3158         m_page->goForward();
3159     else
3160         [m_view _web_superSwipeWithEvent:event];
3161 }
3162
3163 void WebViewImpl::magnifyWithEvent(NSEvent *event)
3164 {
3165     if (!m_allowsMagnification) {
3166 #if ENABLE(MAC_GESTURE_EVENTS)
3167         NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
3168         m_page->handleGestureEvent(webEvent);
3169 #endif
3170         [m_view _web_superMagnifyWithEvent:event];
3171         return;
3172     }
3173
3174     dismissContentRelativeChildWindowsWithAnimation(false);
3175
3176     auto& gestureController = ensureGestureController();
3177
3178 #if ENABLE(MAC_GESTURE_EVENTS)
3179     if (gestureController.hasActiveMagnificationGesture()) {
3180         gestureController.handleMagnificationGestureEvent(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
3181         return;
3182     }
3183
3184     NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
3185     m_page->handleGestureEvent(webEvent);
3186 #else
3187     gestureController.handleMagnificationGestureEvent(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
3188 #endif
3189 }
3190
3191 void WebViewImpl::smartMagnifyWithEvent(NSEvent *event)
3192 {
3193     if (!m_allowsMagnification) {
3194         [m_view _web_superSmartMagnifyWithEvent:event];
3195         return;
3196     }
3197
3198     dismissContentRelativeChildWindowsWithAnimation(false);
3199
3200     ensureGestureController().handleSmartMagnificationGesture([m_view convertPoint:event.locationInWindow fromView:nil]);
3201 }
3202
3203 void WebViewImpl::setLastMouseDownEvent(NSEvent *event)
3204 {
3205     ASSERT(!event || event.type == NSLeftMouseDown || event.type == NSRightMouseDown || event.type == NSOtherMouseDown);
3206
3207     if (event == m_lastMouseDownEvent.get())
3208         return;
3209
3210     m_lastMouseDownEvent = event;
3211 }
3212
3213 #if ENABLE(MAC_GESTURE_EVENTS)
3214 void WebViewImpl::rotateWithEvent(NSEvent *event)
3215 {
3216     NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
3217     m_page->handleGestureEvent(webEvent);
3218 }
3219 #endif
3220
3221 void WebViewImpl::gestureEventWasNotHandledByWebCore(NSEvent *event)
3222 {
3223     [m_view _web_gestureEventWasNotHandledByWebCore:event];
3224 }
3225
3226 void WebViewImpl::gestureEventWasNotHandledByWebCoreFromViewOnly(NSEvent *event)
3227 {
3228 #if ENABLE(MAC_GESTURE_EVENTS)
3229     if (m_gestureController)
3230         m_gestureController->gestureEventWasNotHandledByWebCore(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
3231 #endif
3232 }
3233
3234 void WebViewImpl::doneWithKeyEvent(NSEvent *event, bool eventWasHandled)
3235 {
3236     if ([event type] != NSKeyDown)
3237         return;
3238
3239     if (tryPostProcessPluginComplexTextInputKeyDown(event))
3240         return;
3241
3242     if (eventWasHandled) {
3243         [NSCursor setHiddenUntilMouseMoves:YES];
3244         return;
3245     }
3246
3247     // resending the event may destroy this WKView
3248     RetainPtr<NSView> protector(m_view);
3249
3250     ASSERT(!m_keyDownEventBeingResent);
3251     m_keyDownEventBeingResent = event;
3252     [NSApp _setCurrentEvent:event];
3253     [NSApp sendEvent:event];
3254     
3255     m_keyDownEventBeingResent = nullptr;
3256 }
3257
3258 NSArray *WebViewImpl::validAttributesForMarkedText()
3259 {
3260     static NSArray *validAttributes;
3261     if (!validAttributes) {
3262         validAttributes = @[ NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, NSMarkedClauseSegmentAttributeName,
3263 #if USE(DICTATION_ALTERNATIVES)
3264             NSTextAlternativesAttributeName,
3265 #endif
3266 #if USE(INSERTION_UNDO_GROUPING)
3267             NSTextInsertionUndoableAttributeName,
3268 #endif
3269         ];
3270         // NSText also supports the following attributes, but it's
3271         // hard to tell which are really required for text input to
3272         // work well; I have not seen any input method make use of them yet.
3273         //     NSFontAttributeName, NSForegroundColorAttributeName,
3274         //     NSBackgroundColorAttributeName, NSLanguageAttributeName.
3275         CFRetain(validAttributes);
3276     }
3277     LOG(TextInput, "validAttributesForMarkedText -> (...)");
3278     return validAttributes;
3279 }
3280
3281 static Vector<WebCore::CompositionUnderline> extractUnderlines(NSAttributedString *string)
3282 {
3283     Vector<WebCore::CompositionUnderline> result;
3284     int length = string.string.length;
3285
3286     for (int i = 0; i < length;) {
3287         NSRange range;
3288         NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
3289
3290         if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
3291             WebCore::Color color = WebCore::Color::black;
3292             if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
3293                 color = WebCore::colorFromNSColor(colorAttr);
3294             result.append(WebCore::CompositionUnderline(range.location, NSMaxRange(range), color, style.intValue > 1));
3295         }
3296         
3297         i = range.location + range.length;
3298     }
3299
3300     return result;
3301 }
3302
3303 static bool eventKeyCodeIsZeroOrNumLockOrFn(NSEvent *event)
3304 {
3305     unsigned short keyCode = [event keyCode];
3306     return !keyCode || keyCode == 10 || keyCode == 63;
3307 }
3308
3309 #if USE(ASYNC_NSTEXTINPUTCLIENT)
3310
3311 Vector<WebCore::KeypressCommand> WebViewImpl::collectKeyboardLayoutCommandsForEvent(NSEvent *event)
3312 {
3313     Vector<WebCore::KeypressCommand> commands;
3314
3315     if ([event type] != NSKeyDown)
3316         return commands;
3317
3318     ASSERT(!m_collectedKeypressCommands);
3319     m_collectedKeypressCommands = &commands;
3320
3321     if (NSTextInputContext *context = inputContext())
3322         [context handleEventByKeyboardLayout:event];
3323     else
3324         [m_view interpretKeyEvents:[NSArray arrayWithObject:event]];
3325
3326     m_collectedKeypressCommands = nullptr;
3327
3328     return commands;
3329 }
3330
3331 void WebViewImpl::interpretKeyEvent(NSEvent *event, void(^completionHandler)(BOOL handled, const Vector<WebCore::KeypressCommand>& commands))
3332 {
3333     // For regular Web content, input methods run before passing a keydown to DOM, but plug-ins get an opportunity to handle the event first.
3334     // There is no need to collect commands, as the plug-in cannot execute them.
3335     if (pluginComplexTextInputIdentifier()) {
3336         completionHandler(NO, { });
3337         return;
3338     }
3339
3340     if (!inputContext()) {
3341         auto commands = collectKeyboardLayoutCommandsForEvent(event);
3342         completionHandler(NO, commands);
3343         return;
3344     }
3345
3346     LOG(TextInput, "-> handleEventByInputMethod:%p %@", event, event);
3347     [inputContext() handleEventByInputMethod:event completionHandler:^(BOOL handled) {
3348         
3349         LOG(TextInput, "... handleEventByInputMethod%s handled", handled ? "" : " not");
3350         if (handled) {
3351             completionHandler(YES, { });
3352             return;
3353         }
3354
3355         auto commands = collectKeyboardLayoutCommandsForEvent(event);
3356         completionHandler(NO, commands);
3357     }];
3358 }
3359
3360 void WebViewImpl::doCommandBySelector(SEL selector)
3361 {
3362     LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
3363
3364     if (auto* keypressCommands = m_collectedKeypressCommands) {
3365         WebCore::KeypressCommand command(NSStringFromSelector(selector));
3366         keypressCommands->append(command);
3367         LOG(TextInput, "...stored");
3368         m_page->registerKeypressCommandName(command.commandName);
3369     } else {
3370         // FIXME: Send the command to Editor synchronously and only send it along the
3371         // responder chain if it's a selector that does not correspond to an editing command.
3372         [m_view _web_superDoCommandBySelector:selector];
3373     }
3374 }
3375
3376 void WebViewImpl::insertText(id string)
3377 {
3378     // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
3379     // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
3380     // command ensures that a keypress event is dispatched as appropriate.
3381     insertText(string, NSMakeRange(NSNotFound, 0));
3382 }
3383
3384 void WebViewImpl::insertText(id string, NSRange replacementRange)
3385 {
3386     BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3387     ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
3388
3389     if (replacementRange.location != NSNotFound)
3390         LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
3391     else
3392         LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
3393
3394     NSString *text;
3395     Vector<WebCore::TextAlternativeWithRange> dictationAlternatives;
3396
3397     bool registerUndoGroup = false;
3398     if (isAttributedString) {
3399 #if USE(DICTATION_ALTERNATIVES)
3400         WebCore::collectDictationTextAlternatives(string, dictationAlternatives);
3401 #endif
3402 #if USE(INSERTION_UNDO_GROUPING)
3403         registerUndoGroup = WebCore::shouldRegisterInsertionUndoGroup(string);
3404 #endif
3405         // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
3406         text = [string string];
3407     } else
3408         text = string;
3409
3410     // insertText can be called for several reasons:
3411     // - If it's from normal key event processing (including key bindings), we save the action to perform it later.
3412     // - If it's from an input method, then we should insert the text now.
3413     // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
3414     // then we also execute it immediately, as there will be no other chance.
3415     Vector<WebCore::KeypressCommand>* keypressCommands = m_collectedKeypressCommands;
3416     if (keypressCommands) {
3417         ASSERT(replacementRange.location == NSNotFound);
3418         WebCore::KeypressCommand command("insertText:", text);
3419         keypressCommands->append(command);
3420         LOG(TextInput, "...stored");
3421         m_page->registerKeypressCommandName(command.commandName);
3422         return;
3423     }
3424
3425     String eventText = text;
3426     eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
3427     if (!dictationAlternatives.isEmpty())
3428         m_page->insertDictatedTextAsync(eventText, replacementRange, dictationAlternatives, registerUndoGroup);
3429     else
3430         m_page->insertTextAsync(eventText, replacementRange, registerUndoGroup);
3431 }
3432
3433 void WebViewImpl::selectedRangeWithCompletionHandler(void(^completionHandlerPtr)(NSRange selectedRange))
3434 {
3435     auto completionHandler = adoptNS([completionHandlerPtr copy]);
3436
3437     LOG(TextInput, "selectedRange");
3438     m_page->getSelectedRangeAsync([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, "    ...selectedRange failed.");
3442             completionHandlerBlock(NSMakeRange(NSNotFound, 0));
3443             return;
3444         }
3445         NSRange result = editingRangeResult;
3446         if (result.location == NSNotFound)
3447             LOG(TextInput, "    -> selectedRange returned (NSNotFound, %llu)", result.length);
3448         else
3449             LOG(TextInput, "    -> selectedRange returned (%llu, %llu)", result.location, result.length);
3450         completionHandlerBlock(result);
3451     });
3452 }
3453
3454 void WebViewImpl::markedRangeWithCompletionHandler(void(^completionHandlerPtr)(NSRange markedRange))
3455 {
3456     auto completionHandler = adoptNS([completionHandlerPtr copy]);
3457
3458     LOG(TextInput, "markedRange");
3459     m_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
3460         void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
3461         if (error != WebKit::CallbackBase::Error::None) {
3462             LOG(TextInput, "    ...markedRange failed.");
3463             completionHandlerBlock(NSMakeRange(NSNotFound, 0));
3464             return;
3465         }
3466         NSRange result = editingRangeResult;
3467         if (result.location == NSNotFound)
3468             LOG(TextInput, "    -> markedRange returned (NSNotFound, %llu)", result.length);
3469         else
3470             LOG(TextInput, "    -> markedRange returned (%llu, %llu)", result.location, result.length);
3471         completionHandlerBlock(result);
3472     });
3473 }
3474
3475 void WebViewImpl::hasMarkedTextWithCompletionHandler(void(^completionHandlerPtr)(BOOL hasMarkedText))
3476 {
3477     auto completionHandler = adoptNS([completionHandlerPtr copy]);
3478
3479     LOG(TextInput, "hasMarkedText");
3480     m_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
3481         void (^completionHandlerBlock)(BOOL) = (void (^)(BOOL))completionHandler.get();
3482         if (error != WebKit::CallbackBase::Error::None) {
3483             LOG(TextInput, "    ...hasMarkedText failed.");
3484             completionHandlerBlock(NO);
3485             return;
3486         }
3487         BOOL hasMarkedText = editingRangeResult.location != notFound;
3488         LOG(TextInput, "    -> hasMarkedText returned %u", hasMarkedText);
3489         completionHandlerBlock(hasMarkedText);
3490     });
3491 }
3492
3493 void WebViewImpl::attributedSubstringForProposedRange(NSRange proposedRange, void(^completionHandlerPtr)(NSAttributedString *attrString, NSRange actualRange))
3494 {
3495     auto completionHandler = adoptNS([completionHandlerPtr copy]);
3496
3497     LOG(TextInput, "attributedSubstringFromRange:(%u, %u)", proposedRange.location, proposedRange.length);
3498     m_page->attributedSubstringForCharacterRangeAsync(proposedRange, [completionHandler](const AttributedString& string, const EditingRange& actualRange, WebKit::CallbackBase::Error error) {
3499         void (^completionHandlerBlock)(NSAttributedString *, NSRange) = (void (^)(NSAttributedString *, NSRange))completionHandler.get();
3500         if (error != WebKit::CallbackBase::Error::None) {
3501             LOG(TextInput, "    ...attributedSubstringFromRange failed.");
3502             completionHandlerBlock(0, NSMakeRange(NSNotFound, 0));
3503             return;
3504         }
3505         LOG(TextInput, "    -> attributedSubstringFromRange returned %@", [string.string.get() string]);