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