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