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