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