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