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