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