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