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