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