2 * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "WebViewImpl.h"
31 #import "APILegacyContextHistoryClient.h"
32 #import "ColorSpaceData.h"
33 #import "GenericCallback.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"
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>
99 #import <wtf/NeverDestroyed.h>
100 #import <wtf/SetForScope.h>
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)
107 static NSString * const WKMediaExitFullScreenItem = @"WKMediaExitFullScreenItem";
108 #endif // HAVE(TOUCH_BAR) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
110 SOFT_LINK_CONSTANT_MAY_FAIL(Lookup, LUNotificationPopoverWillClose, NSString *)
112 @interface NSApplication ()
114 - (void)speakString:(NSString *)string;
115 - (void)stopSpeaking:(id)sender;
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;
125 @interface WKAccessibilitySettingsObserver : NSObject {
126 WebKit::WebViewImpl *_impl;
129 - (instancetype)initWithImpl:(WebKit::WebViewImpl&)impl;
132 @implementation WKAccessibilitySettingsObserver
134 - (instancetype)initWithImpl:(WebKit::WebViewImpl&)impl
142 NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
143 [workspaceNotificationCenter addObserver:self selector:@selector(_settingsDidChange:) name:NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification object:nil];
150 NSNotificationCenter *workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
151 [workspaceNotificationCenter removeObserver:self name:NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification object:nil];
156 - (void)_settingsDidChange:(NSNotification *)notification
158 WTFLogAlways("received notification");
160 _impl->accessibilitySettingsDidChange();
165 @interface WKWindowVisibilityObserver : NSObject {
167 WebKit::WebViewImpl *_impl;
170 - (instancetype)initWithView:(NSView *)view impl:(WebKit::WebViewImpl&)impl;
171 - (void)startObserving:(NSWindow *)window;
172 - (void)stopObserving:(NSWindow *)window;
173 - (void)startObservingFontPanel;
174 - (void)startObservingLookupDismissal;
177 @implementation WKWindowVisibilityObserver
179 - (instancetype)initWithView:(NSView *)view impl:(WebKit::WebViewImpl&)impl
188 NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
189 [workspaceNotificationCenter addObserver:self selector:@selector(_activeSpaceDidChange:) name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
196 if (canLoadLUNotificationPopoverWillClose())
197 [[NSNotificationCenter defaultCenter] removeObserver:self name:getLUNotificationPopoverWillClose() object:nil];
199 NSNotificationCenter *workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
200 [workspaceNotificationCenter removeObserver:self name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
205 static void* keyValueObservingContext = &keyValueObservingContext;
207 - (void)startObserving:(NSWindow *)window
212 NSNotificationCenter *defaultNotificationCenter = [NSNotificationCenter defaultCenter];
214 // An NSView derived object such as WKView cannot observe these notifications, because NSView itself observes them.
215 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidOrderOffScreen:) name:@"NSWindowDidOrderOffScreenNotification" object:window];
216 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidOrderOnScreen:) name:@"_NSWindowDidBecomeVisible" object:window];
218 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
219 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
220 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
221 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
222 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidMove:) name:NSWindowDidMoveNotification object:window];
223 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidResize:) name:NSWindowDidResizeNotification object:window];
224 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeBackingProperties:) name:NSWindowDidChangeBackingPropertiesNotification object:window];
225 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification object:window];
226 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeLayerHosting:) name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
227 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeOcclusionState:) name:NSWindowDidChangeOcclusionStateNotification object:window];
229 [window addObserver:self forKeyPath:@"contentLayoutRect" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
230 [window addObserver:self forKeyPath:@"titlebarAppearsTransparent" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
233 - (void)stopObserving:(NSWindow *)window
238 NSNotificationCenter *defaultNotificationCenter = [NSNotificationCenter defaultCenter];
240 [defaultNotificationCenter removeObserver:self name:@"NSWindowDidOrderOffScreenNotification" object:window];
241 [defaultNotificationCenter removeObserver:self name:@"_NSWindowDidBecomeVisible" object:window];
243 [defaultNotificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
244 [defaultNotificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
245 [defaultNotificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
246 [defaultNotificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
247 [defaultNotificationCenter removeObserver:self name:NSWindowDidMoveNotification object:window];
248 [defaultNotificationCenter removeObserver:self name:NSWindowDidResizeNotification object:window];
249 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window];
250 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeScreenNotification object:window];
251 [defaultNotificationCenter removeObserver:self name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
252 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeOcclusionStateNotification object:window];
254 if (_impl->isEditable())
255 [[NSFontPanel sharedFontPanel] removeObserver:self forKeyPath:@"visible" context:keyValueObservingContext];
256 [window removeObserver:self forKeyPath:@"contentLayoutRect" context:keyValueObservingContext];
257 [window removeObserver:self forKeyPath:@"titlebarAppearsTransparent" context:keyValueObservingContext];
260 - (void)startObservingFontPanel
262 [[NSFontPanel sharedFontPanel] addObserver:self forKeyPath:@"visible" options:0 context:keyValueObservingContext];
265 - (void)startObservingLookupDismissal
267 if (canLoadLUNotificationPopoverWillClose())
268 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_dictionaryLookupPopoverWillClose:) name:getLUNotificationPopoverWillClose() object:nil];
271 - (void)_windowDidOrderOnScreen:(NSNotification *)notification
273 _impl->windowDidOrderOnScreen();
276 - (void)_windowDidOrderOffScreen:(NSNotification *)notification
278 _impl->windowDidOrderOffScreen();
281 - (void)_windowDidBecomeKey:(NSNotification *)notification
283 _impl->windowDidBecomeKey([notification object]);
286 - (void)_windowDidResignKey:(NSNotification *)notification
288 _impl->windowDidResignKey([notification object]);
291 - (void)_windowDidMiniaturize:(NSNotification *)notification
293 _impl->windowDidMiniaturize();
296 - (void)_windowDidDeminiaturize:(NSNotification *)notification
298 _impl->windowDidDeminiaturize();
301 - (void)_windowDidMove:(NSNotification *)notification
303 _impl->windowDidMove();
306 - (void)_windowDidResize:(NSNotification *)notification
308 _impl->windowDidResize();
311 - (void)_windowDidChangeBackingProperties:(NSNotification *)notification
313 CGFloat oldBackingScaleFactor = [[notification.userInfo objectForKey:NSBackingPropertyOldScaleFactorKey] doubleValue];
314 _impl->windowDidChangeBackingProperties(oldBackingScaleFactor);
317 - (void)_windowDidChangeScreen:(NSNotification *)notification
319 _impl->windowDidChangeScreen();
322 - (void)_windowDidChangeLayerHosting:(NSNotification *)notification
324 _impl->windowDidChangeLayerHosting();
327 - (void)_windowDidChangeOcclusionState:(NSNotification *)notification
329 _impl->windowDidChangeOcclusionState();
332 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
334 if (context != keyValueObservingContext) {
335 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
339 if ([keyPath isEqualToString:@"visible"] && [NSFontPanel sharedFontPanelExists] && object == [NSFontPanel sharedFontPanel]) {
340 _impl->updateFontPanelIfNeeded();
343 if ([keyPath isEqualToString:@"contentLayoutRect"] || [keyPath isEqualToString:@"titlebarAppearsTransparent"])
344 _impl->updateContentInsetsIfAutomatic();
347 - (void)_dictionaryLookupPopoverWillClose:(NSNotification *)notification
349 _impl->clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::None);
352 - (void)_activeSpaceDidChange:(NSNotification *)notification
354 _impl->activeSpaceDidChange();
359 @interface WKEditCommandObjC : NSObject {
360 RefPtr<WebKit::WebEditCommandProxy> m_command;
362 - (id)initWithWebEditCommandProxy:(RefPtr<WebKit::WebEditCommandProxy>)command;
363 - (WebKit::WebEditCommandProxy*)command;
366 @interface WKEditorUndoTargetObjC : NSObject
367 - (void)undoEditing:(id)sender;
368 - (void)redoEditing:(id)sender;
371 @implementation WKEditCommandObjC
373 - (id)initWithWebEditCommandProxy:(RefPtr<WebKit::WebEditCommandProxy>)command
383 - (WebKit::WebEditCommandProxy*)command
385 return m_command.get();
390 @implementation WKEditorUndoTargetObjC
392 - (void)undoEditing:(id)sender
394 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
395 [sender command]->unapply();
398 - (void)redoEditing:(id)sender
400 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
401 [sender command]->reapply();
406 @interface WKFlippedView : NSView
409 @implementation WKFlippedView
418 @interface WKResponderChainSink : NSResponder {
419 NSResponder *_lastResponderInChain;
420 bool _didReceiveUnhandledCommand;
423 - (id)initWithResponderChain:(NSResponder *)chain;
425 - (bool)didReceiveUnhandledCommand;
428 @implementation WKResponderChainSink
430 - (id)initWithResponderChain:(NSResponder *)chain
435 _lastResponderInChain = chain;
436 while (NSResponder *next = [_lastResponderInChain nextResponder])
437 _lastResponderInChain = next;
438 [_lastResponderInChain setNextResponder:self];
444 // This assumes that the responder chain was either unmodified since
445 // -initWithResponderChain: was called, or was modified in such a way
446 // that _lastResponderInChain is still in the chain, and self was not
447 // moved earlier in the chain than _lastResponderInChain.
448 NSResponder *responderBeforeSelf = _lastResponderInChain;
449 NSResponder *next = [responderBeforeSelf nextResponder];
450 for (; next && next != self; next = [next nextResponder])
451 responderBeforeSelf = next;
453 // Nothing to be done if we are no longer in the responder chain.
457 [responderBeforeSelf setNextResponder:[self nextResponder]];
458 _lastResponderInChain = nil;
461 - (bool)didReceiveUnhandledCommand
463 return _didReceiveUnhandledCommand;
466 - (void)noResponderFor:(SEL)selector
468 _didReceiveUnhandledCommand = true;
471 - (void)doCommandBySelector:(SEL)selector
473 _didReceiveUnhandledCommand = true;
476 - (BOOL)tryToPerform:(SEL)action with:(id)object
478 _didReceiveUnhandledCommand = true;
484 using namespace WebKit;
488 @interface WKTextListTouchBarViewController : NSViewController {
490 WebViewImpl* _webViewImpl;
491 ListType _currentListType;
494 @property (nonatomic) ListType currentListType;
496 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl;
500 @implementation WKTextListTouchBarViewController
502 @synthesize currentListType=_currentListType;
504 static const CGFloat listControlSegmentWidth = 67;
505 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER) && ENABLE(FULLSCREEN_API)
506 static const CGFloat exitFullScreenButtonWidth = 64;
509 static const NSUInteger noListSegment = 0;
510 static const NSUInteger unorderedListSegment = 1;
511 static const NSUInteger orderedListSegment = 2;
513 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl
515 if (!(self = [super init]))
518 _webViewImpl = webViewImpl;
520 NSSegmentedControl *insertListControl = [NSSegmentedControl segmentedControlWithLabels:@[ WebCore::insertListTypeNone(), WebCore::insertListTypeBulleted(), WebCore::insertListTypeNumbered() ] trackingMode:NSSegmentSwitchTrackingSelectOne target:self action:@selector(_selectList:)];
521 [insertListControl setWidth:listControlSegmentWidth forSegment:noListSegment];
522 [insertListControl setWidth:listControlSegmentWidth forSegment:unorderedListSegment];
523 [insertListControl setWidth:listControlSegmentWidth forSegment:orderedListSegment];
524 insertListControl.font = [NSFont systemFontOfSize:15];
526 #pragma clang diagnostic push
527 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
528 id segmentElement = NSAccessibilityUnignoredDescendant(insertListControl);
529 NSArray *segments = [segmentElement accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
530 ASSERT(segments.count == 3);
531 [segments[noListSegment] accessibilitySetOverrideValue:WebCore::insertListTypeNone() forAttribute:NSAccessibilityDescriptionAttribute];
532 [segments[unorderedListSegment] accessibilitySetOverrideValue:WebCore::insertListTypeBulletedAccessibilityTitle() forAttribute:NSAccessibilityDescriptionAttribute];
533 [segments[orderedListSegment] accessibilitySetOverrideValue:WebCore::insertListTypeNumberedAccessibilityTitle() forAttribute:NSAccessibilityDescriptionAttribute];
534 #pragma clang diagnostic pop
536 self.view = insertListControl;
541 - (void)didDestroyView
543 _webViewImpl = nullptr;
546 - (void)_selectList:(id)sender
551 NSSegmentedControl *insertListControl = (NSSegmentedControl *)self.view;
552 switch (insertListControl.selectedSegment) {
554 // There is no "remove list" edit command, but InsertOrderedList and InsertUnorderedList both
555 // behave as toggles, so we can invoke the appropriate edit command depending on our _currentListType
556 // to remove an existing list. We don't have to do anything if _currentListType is NoList.
557 if (_currentListType == OrderedList)
558 _webViewImpl->page().executeEditCommand(@"InsertOrderedList", @"");
559 else if (_currentListType == UnorderedList)
560 _webViewImpl->page().executeEditCommand(@"InsertUnorderedList", @"");
562 case unorderedListSegment:
563 _webViewImpl->page().executeEditCommand(@"InsertUnorderedList", @"");
565 case orderedListSegment:
566 _webViewImpl->page().executeEditCommand(@"InsertOrderedList", @"");
570 _webViewImpl->dismissTextTouchBarPopoverItemWithIdentifier(NSTouchBarItemIdentifierTextList);
573 - (void)setCurrentListType:(ListType)listType
575 NSSegmentedControl *insertListControl = (NSSegmentedControl *)self.view;
578 [insertListControl setSelected:YES forSegment:noListSegment];
581 [insertListControl setSelected:YES forSegment:orderedListSegment];
584 [insertListControl setSelected:YES forSegment:unorderedListSegment];
588 _currentListType = listType;
593 @interface WKTextTouchBarItemController : NSTextTouchBarItemController <NSCandidateListTouchBarItemDelegate, NSTouchBarDelegate> {
597 BOOL _textIsUnderlined;
598 NSTextAlignment _currentTextAlignment;
599 RetainPtr<NSColor> _textColor;
600 RetainPtr<WKTextListTouchBarViewController> _textListTouchBarViewController;
603 WebViewImpl* _webViewImpl;
606 @property (nonatomic) BOOL textIsBold;
607 @property (nonatomic) BOOL textIsItalic;
608 @property (nonatomic) BOOL textIsUnderlined;
609 @property (nonatomic) NSTextAlignment currentTextAlignment;
610 @property (nonatomic, retain, readwrite) NSColor *textColor;
612 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl;
615 @implementation WKTextTouchBarItemController
617 @synthesize textIsBold=_textIsBold;
618 @synthesize textIsItalic=_textIsItalic;
619 @synthesize textIsUnderlined=_textIsUnderlined;
620 @synthesize currentTextAlignment=_currentTextAlignment;
622 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl
624 if (!(self = [super init]))
627 _webViewImpl = webViewImpl;
632 - (void)didDestroyView
634 [[NSNotificationCenter defaultCenter] removeObserver:self];
635 _webViewImpl = nullptr;
636 [_textListTouchBarViewController didDestroyView];
639 #pragma mark NSTouchBarDelegate
641 - (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSString *)identifier
643 return [self itemForIdentifier:identifier];
646 - (nullable NSTouchBarItem *)itemForIdentifier:(NSString *)identifier
648 NSTouchBarItem *item = [super itemForIdentifier:identifier];
649 BOOL isTextFormatItem = [identifier isEqualToString:NSTouchBarItemIdentifierTextFormat];
651 if (isTextFormatItem || [identifier isEqualToString:NSTouchBarItemIdentifierTextStyle])
652 self.textStyle.action = @selector(_wkChangeTextStyle:);
654 if (isTextFormatItem || [identifier isEqualToString:NSTouchBarItemIdentifierTextAlignment])
655 self.textAlignments.action = @selector(_wkChangeTextAlignment:);
657 NSColorPickerTouchBarItem *colorPickerItem = nil;
658 if ([identifier isEqualToString:NSTouchBarItemIdentifierTextColorPicker] && [item isKindOfClass:[NSColorPickerTouchBarItem class]])
659 colorPickerItem = (NSColorPickerTouchBarItem *)item;
660 if (isTextFormatItem)
661 colorPickerItem = self.colorPickerItem;
662 if (colorPickerItem) {
663 colorPickerItem.target = self;
664 colorPickerItem.action = @selector(_wkChangeColor:);
665 colorPickerItem.showsAlpha = NO;
671 #pragma mark NSCandidateListTouchBarItemDelegate
673 - (void)candidateListTouchBarItem:(NSCandidateListTouchBarItem *)anItem endSelectingCandidateAtIndex:(NSInteger)index
675 if (index == NSNotFound)
681 NSArray *candidates = anItem.candidates;
682 if ((NSUInteger)index >= candidates.count)
685 NSTextCheckingResult *candidate = candidates[index];
686 ASSERT([candidate isKindOfClass:[NSTextCheckingResult class]]);
688 _webViewImpl->handleAcceptedCandidate(candidate);
691 - (void)candidateListTouchBarItem:(NSCandidateListTouchBarItem *)anItem changedCandidateListVisibility:(BOOL)isVisible
697 _webViewImpl->requestCandidatesForSelectionIfNeeded();
699 _webViewImpl->updateTouchBar();
702 #pragma mark NSNotificationCenter observers
704 - (void)touchBarDidExitCustomization:(NSNotification *)notification
709 _webViewImpl->setIsCustomizingTouchBar(false);
710 _webViewImpl->updateTouchBar();
713 - (void)touchBarWillEnterCustomization:(NSNotification *)notification
718 _webViewImpl->setIsCustomizingTouchBar(true);
721 - (void)didChangeAutomaticTextCompletion:(NSNotification *)notification
726 _webViewImpl->updateTouchBarAndRefreshTextBarIdentifiers();
730 #pragma mark NSTextTouchBarItemController
732 - (WKTextListTouchBarViewController *)textListTouchBarViewController
734 return (WKTextListTouchBarViewController *)self.textListViewController;
737 - (void)setTextIsBold:(BOOL)bold
740 if ([self.textStyle isSelectedForSegment:0] != _textIsBold)
741 [self.textStyle setSelected:_textIsBold forSegment:0];
744 - (void)setTextIsItalic:(BOOL)italic
746 _textIsItalic = italic;
747 if ([self.textStyle isSelectedForSegment:1] != _textIsItalic)
748 [self.textStyle setSelected:_textIsItalic forSegment:1];
751 - (void)setTextIsUnderlined:(BOOL)underlined
753 _textIsUnderlined = underlined;
754 if ([self.textStyle isSelectedForSegment:2] != _textIsUnderlined)
755 [self.textStyle setSelected:_textIsUnderlined forSegment:2];
758 - (void)_wkChangeTextStyle:(id)sender
763 if ([self.textStyle isSelectedForSegment:0] != _textIsBold) {
764 _textIsBold = !_textIsBold;
765 _webViewImpl->page().executeEditCommand(@"ToggleBold", @"");
768 if ([self.textStyle isSelectedForSegment:1] != _textIsItalic) {
769 _textIsItalic = !_textIsItalic;
770 _webViewImpl->page().executeEditCommand("ToggleItalic", @"");
773 if ([self.textStyle isSelectedForSegment:2] != _textIsUnderlined) {
774 _textIsUnderlined = !_textIsUnderlined;
775 _webViewImpl->page().executeEditCommand("ToggleUnderline", @"");
779 - (void)setCurrentTextAlignment:(NSTextAlignment)alignment
781 _currentTextAlignment = alignment;
782 [self.textAlignments selectSegmentWithTag:_currentTextAlignment];
785 - (void)_wkChangeTextAlignment:(id)sender
790 NSTextAlignment alignment = (NSTextAlignment)[self.textAlignments.cell tagForSegment:self.textAlignments.selectedSegment];
792 case NSTextAlignmentLeft:
793 _currentTextAlignment = NSTextAlignmentLeft;
794 _webViewImpl->page().executeEditCommand("AlignLeft", @"");
796 case NSTextAlignmentRight:
797 _currentTextAlignment = NSTextAlignmentRight;
798 _webViewImpl->page().executeEditCommand("AlignRight", @"");
800 case NSTextAlignmentCenter:
801 _currentTextAlignment = NSTextAlignmentCenter;
802 _webViewImpl->page().executeEditCommand("AlignCenter", @"");
804 case NSTextAlignmentJustified:
805 _currentTextAlignment = NSTextAlignmentJustified;
806 _webViewImpl->page().executeEditCommand("AlignJustified", @"");
812 _webViewImpl->dismissTextTouchBarPopoverItemWithIdentifier(NSTouchBarItemIdentifierTextAlignment);
815 - (NSColor *)textColor
817 return _textColor.get();
820 - (void)setTextColor:(NSColor *)color
823 self.colorPickerItem.color = _textColor.get();
826 - (void)_wkChangeColor:(id)sender
831 _textColor = self.colorPickerItem.color;
832 _webViewImpl->page().executeEditCommand("ForeColor", WebCore::colorFromNSColor(_textColor.get()).serialized());
835 - (NSViewController *)textListViewController
837 if (!_textListTouchBarViewController)
838 _textListTouchBarViewController = adoptNS([[WKTextListTouchBarViewController alloc] initWithWebViewImpl:_webViewImpl]);
839 return _textListTouchBarViewController.get();
846 NSTouchBar *WebViewImpl::makeTouchBar()
848 if (!m_canCreateTouchBars) {
849 m_canCreateTouchBars = true;
852 return m_currentTouchBar.get();
855 void WebViewImpl::updateTouchBar()
857 if (!m_canCreateTouchBars)
860 NSTouchBar *touchBar = nil;
861 bool userActionRequirementsHaveBeenMet = m_requiresUserActionForEditingControlsManager ? m_page->hasHadSelectionChangesFromUserInteraction() : true;
862 if (m_page->editorState().isContentEditable && !m_page->needsHiddenContentEditableQuirk()) {
863 updateTextTouchBar();
864 if (userActionRequirementsHaveBeenMet)
865 touchBar = textTouchBar();
867 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
868 else if (m_page->hasActiveVideoForControlsManager()) {
869 updateMediaTouchBar();
870 // If useMediaPlaybackControlsView() is true, then we are relying on the API client to display a popover version
871 // of the media timeline in their own function bar. If it is false, then we will display the media timeline in our
873 if (!useMediaPlaybackControlsView())
874 touchBar = [m_mediaTouchBarProvider respondsToSelector:@selector(touchBar)] ? [(id)m_mediaTouchBarProvider.get() touchBar] : [(id)m_mediaTouchBarProvider.get() touchBar];
875 } else if ([m_mediaTouchBarProvider playbackControlsController]) {
876 if (m_clientWantsMediaPlaybackControlsView) {
877 if ([m_view respondsToSelector:@selector(_web_didRemoveMediaControlsManager)] && m_view == m_view.window.firstResponder)
878 [m_view _web_didRemoveMediaControlsManager];
880 [m_mediaTouchBarProvider setPlaybackControlsController:nil];
881 [m_mediaPlaybackControlsView setPlaybackControlsController:nil];
885 if (touchBar == m_currentTouchBar)
888 // If m_editableElementIsFocused is true, then we may have a non-editable selection right now just because
889 // the user is clicking or tabbing between editable fields.
890 if (m_editableElementIsFocused && touchBar != textTouchBar())
893 m_currentTouchBar = touchBar;
894 [m_view willChangeValueForKey:@"touchBar"];
895 [m_view setTouchBar:m_currentTouchBar.get()];
896 [m_view didChangeValueForKey:@"touchBar"];
899 NSCandidateListTouchBarItem *WebViewImpl::candidateListTouchBarItem() const
901 return isRichlyEditable() ? m_richTextCandidateListTouchBarItem.get() : m_plainTextCandidateListTouchBarItem.get();
904 AVFunctionBarScrubber *WebViewImpl::mediaPlaybackControlsView() const
906 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
907 if (m_page->hasActiveVideoForControlsManager())
908 return m_mediaPlaybackControlsView.get();
913 bool WebViewImpl::useMediaPlaybackControlsView() const
915 #if ENABLE(FULLSCREEN_API)
916 if (hasFullScreenWindowController())
917 return ![m_fullScreenWindowController isFullScreen];
919 return m_clientWantsMediaPlaybackControlsView;
922 void WebViewImpl::dismissTextTouchBarPopoverItemWithIdentifier(NSString *identifier)
924 NSTouchBarItem *foundItem = nil;
925 for (NSTouchBarItem *item in textTouchBar().items) {
926 if ([item.identifier isEqualToString:identifier]) {
931 if ([item.identifier isEqualToString:NSTouchBarItemIdentifierTextFormat]) {
932 for (NSTouchBarItem *childItem in ((NSGroupTouchBarItem *)item).groupTouchBar.items) {
933 if ([childItem.identifier isEqualToString:identifier]) {
934 foundItem = childItem;
942 if ([foundItem isKindOfClass:[NSPopoverTouchBarItem class]])
943 [(NSPopoverTouchBarItem *)foundItem dismissPopover:nil];
946 static NSArray<NSString *> *textTouchBarCustomizationAllowedIdentifiers()
948 return @[ NSTouchBarItemIdentifierCharacterPicker, NSTouchBarItemIdentifierTextColorPicker, NSTouchBarItemIdentifierTextStyle, NSTouchBarItemIdentifierTextAlignment, NSTouchBarItemIdentifierTextList, NSTouchBarItemIdentifierFlexibleSpace ];
951 static NSArray<NSString *> *plainTextTouchBarDefaultItemIdentifiers()
953 return @[ NSTouchBarItemIdentifierCharacterPicker, NSTouchBarItemIdentifierCandidateList ];
956 static NSArray<NSString *> *richTextTouchBarDefaultItemIdentifiers()
958 return @[ NSTouchBarItemIdentifierCharacterPicker, NSTouchBarItemIdentifierTextFormat, NSTouchBarItemIdentifierCandidateList ];
961 void WebViewImpl::updateTouchBarAndRefreshTextBarIdentifiers()
963 if (m_richTextTouchBar)
964 setUpTextTouchBar(m_richTextTouchBar.get());
966 if (m_plainTextTouchBar)
967 setUpTextTouchBar(m_plainTextTouchBar.get());
972 void WebViewImpl::setUpTextTouchBar(NSTouchBar *touchBar)
974 bool isRichTextTouchBar = touchBar == m_richTextTouchBar.get();
975 [touchBar setDelegate:m_textTouchBarItemController.get()];
976 [touchBar setTemplateItems:[NSMutableSet setWithObject:isRichTextTouchBar ? m_richTextCandidateListTouchBarItem.get() : m_plainTextCandidateListTouchBarItem.get()]];
977 [touchBar setCustomizationAllowedItemIdentifiers:textTouchBarCustomizationAllowedIdentifiers()];
978 [touchBar setDefaultItemIdentifiers:isRichTextTouchBar ? richTextTouchBarDefaultItemIdentifiers() : plainTextTouchBarDefaultItemIdentifiers()];
980 if (NSGroupTouchBarItem *textFormatItem = (NSGroupTouchBarItem *)[touchBar itemForIdentifier:NSTouchBarItemIdentifierTextFormat])
981 textFormatItem.groupTouchBar.customizationIdentifier = @"WKTextFormatTouchBar";
984 bool WebViewImpl::isRichlyEditable() const
986 return m_page->editorState().isContentRichlyEditable && !m_page->needsPlainTextQuirk();
989 NSTouchBar *WebViewImpl::textTouchBar() const
991 return isRichlyEditable() ? m_richTextTouchBar.get() : m_plainTextTouchBar.get();
994 static NSTextAlignment nsTextAlignmentFromTextAlignment(TextAlignment textAlignment)
996 NSTextAlignment nsTextAlignment;
997 switch (textAlignment) {
999 nsTextAlignment = NSTextAlignmentNatural;
1002 nsTextAlignment = NSTextAlignmentLeft;
1004 case RightAlignment:
1005 nsTextAlignment = NSTextAlignmentRight;
1007 case CenterAlignment:
1008 nsTextAlignment = NSTextAlignmentCenter;
1010 case JustifiedAlignment:
1011 nsTextAlignment = NSTextAlignmentJustified;
1014 ASSERT_NOT_REACHED();
1017 return nsTextAlignment;
1020 void WebViewImpl::updateTextTouchBar()
1022 if (!m_page->editorState().isContentEditable)
1025 if (m_isUpdatingTextTouchBar)
1028 SetForScope<bool> isUpdatingTextFunctionBar(m_isUpdatingTextTouchBar, true);
1030 if (!m_textTouchBarItemController)
1031 m_textTouchBarItemController = adoptNS([[WKTextTouchBarItemController alloc] initWithWebViewImpl:this]);
1033 if (!m_startedListeningToCustomizationEvents) {
1034 [[NSNotificationCenter defaultCenter] addObserver:m_textTouchBarItemController.get() selector:@selector(touchBarDidExitCustomization:) name:NSTouchBarDidExitCustomization object:nil];
1035 [[NSNotificationCenter defaultCenter] addObserver:m_textTouchBarItemController.get() selector:@selector(touchBarWillEnterCustomization:) name:NSTouchBarWillEnterCustomization object:nil];
1036 [[NSNotificationCenter defaultCenter] addObserver:m_textTouchBarItemController.get() selector:@selector(didChangeAutomaticTextCompletion:) name:NSSpellCheckerDidChangeAutomaticTextCompletionNotification object:nil];
1038 m_startedListeningToCustomizationEvents = true;
1041 if (!m_richTextCandidateListTouchBarItem || !m_plainTextCandidateListTouchBarItem) {
1042 m_richTextCandidateListTouchBarItem = adoptNS([[NSCandidateListTouchBarItem alloc] initWithIdentifier:NSTouchBarItemIdentifierCandidateList]);
1043 [m_richTextCandidateListTouchBarItem setDelegate:m_textTouchBarItemController.get()];
1044 m_plainTextCandidateListTouchBarItem = adoptNS([[NSCandidateListTouchBarItem alloc] initWithIdentifier:NSTouchBarItemIdentifierCandidateList]);
1045 [m_plainTextCandidateListTouchBarItem setDelegate:m_textTouchBarItemController.get()];
1046 requestCandidatesForSelectionIfNeeded();
1049 if (!m_richTextTouchBar) {
1050 m_richTextTouchBar = adoptNS([[NSTouchBar alloc] init]);
1051 setUpTextTouchBar(m_richTextTouchBar.get());
1052 [m_richTextTouchBar setCustomizationIdentifier:@"WKRichTextTouchBar"];
1055 if (!m_plainTextTouchBar) {
1056 m_plainTextTouchBar = adoptNS([[NSTouchBar alloc] init]);
1057 setUpTextTouchBar(m_plainTextTouchBar.get());
1058 [m_plainTextTouchBar setCustomizationIdentifier:@"WKPlainTextTouchBar"];
1061 if ([NSSpellChecker isAutomaticTextCompletionEnabled] && !m_isCustomizingTouchBar) {
1062 BOOL showCandidatesList = !m_page->editorState().selectionIsRange || m_isHandlingAcceptedCandidate;
1063 [candidateListTouchBarItem() updateWithInsertionPointVisibility:showCandidatesList];
1064 [m_view _didUpdateCandidateListVisibility:showCandidatesList];
1067 if (m_page->editorState().isInPasswordField) {
1068 // We don't request candidates for password fields. If the user was previously in a non-password field, then the
1069 // old candidates will still show by default, so we clear them here by setting an empty array of candidates.
1070 if (!m_emptyCandidatesArray)
1071 m_emptyCandidatesArray = adoptNS([[NSArray alloc] init]);
1072 [candidateListTouchBarItem() setCandidates:m_emptyCandidatesArray.get() forSelectedRange:NSMakeRange(0, 0) inString:nil];
1075 NSTouchBar *textTouchBar = this->textTouchBar();
1076 BOOL isShowingCombinedTextFormatItem = [textTouchBar.defaultItemIdentifiers containsObject:NSTouchBarItemIdentifierTextFormat];
1077 [textTouchBar setPrincipalItemIdentifier:isShowingCombinedTextFormatItem ? NSTouchBarItemIdentifierTextFormat : nil];
1079 // Set current typing attributes for rich text. This will ensure that the buttons reflect the state of
1080 // the text when changing selection throughout the document.
1081 if (isRichlyEditable()) {
1082 const EditorState& editorState = m_page->editorState();
1083 if (!editorState.isMissingPostLayoutData) {
1084 [m_textTouchBarItemController setTextIsBold:(bool)(m_page->editorState().postLayoutData().typingAttributes & AttributeBold)];
1085 [m_textTouchBarItemController setTextIsItalic:(bool)(m_page->editorState().postLayoutData().typingAttributes & AttributeItalics)];
1086 [m_textTouchBarItemController setTextIsUnderlined:(bool)(m_page->editorState().postLayoutData().typingAttributes & AttributeUnderline)];
1087 [m_textTouchBarItemController setTextColor:nsColor(editorState.postLayoutData().textColor)];
1088 [[m_textTouchBarItemController textListTouchBarViewController] setCurrentListType:(ListType)m_page->editorState().postLayoutData().enclosingListType];
1089 [m_textTouchBarItemController setCurrentTextAlignment:nsTextAlignmentFromTextAlignment((TextAlignment)editorState.postLayoutData().textAlignment)];
1091 BOOL isShowingCandidateListItem = [textTouchBar.defaultItemIdentifiers containsObject:NSTouchBarItemIdentifierCandidateList] && [NSSpellChecker isAutomaticTextReplacementEnabled];
1092 [m_textTouchBarItemController setUsesNarrowTextStyleItem:isShowingCombinedTextFormatItem && isShowingCandidateListItem];
1096 void WebViewImpl::updateMediaTouchBar()
1098 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
1099 if (!m_mediaTouchBarProvider)
1100 m_mediaTouchBarProvider = adoptNS([allocAVFunctionBarPlaybackControlsProviderInstance() init]);
1102 if (!m_mediaPlaybackControlsView)
1103 m_mediaPlaybackControlsView = adoptNS([allocAVFunctionBarScrubberInstance() init]);
1105 if (!m_playbackControlsManager)
1106 m_playbackControlsManager = adoptNS([[WebPlaybackControlsManager alloc] init]);
1108 if (PlatformWebPlaybackSessionInterface* interface = m_page->playbackSessionManager()->controlsManagerInterface())
1109 [m_playbackControlsManager setWebPlaybackSessionInterfaceMac:interface];
1111 [m_mediaTouchBarProvider setPlaybackControlsController:m_playbackControlsManager.get()];
1112 [m_mediaPlaybackControlsView setPlaybackControlsController:m_playbackControlsManager.get()];
1114 if (!useMediaPlaybackControlsView()) {
1115 #if ENABLE(FULLSCREEN_API)
1116 // If we can't have a media popover function bar item, it might be because we are in full screen.
1117 // If so, customize the escape key.
1118 NSTouchBar *touchBar = [m_mediaTouchBarProvider respondsToSelector:@selector(touchBar)] ? [(id)m_mediaTouchBarProvider.get() touchBar] : [(id)m_mediaTouchBarProvider.get() touchBar];
1119 if (hasFullScreenWindowController() && [m_fullScreenWindowController isFullScreen]) {
1120 if (!m_exitFullScreenButton) {
1121 m_exitFullScreenButton = adoptNS([[NSCustomTouchBarItem alloc] initWithIdentifier:WKMediaExitFullScreenItem]);
1123 NSImage *image = [NSImage imageNamed:NSImageNameTouchBarExitFullScreenTemplate];
1124 [image setTemplate:YES];
1126 NSButton *exitFullScreenButton = [NSButton buttonWithTitle:image ? @"" : @"Exit" image:image target:m_fullScreenWindowController.get() action:@selector(requestExitFullScreen)];
1127 [exitFullScreenButton setAccessibilityTitle:WebCore::exitFullScreenButtonAccessibilityTitle()];
1129 [[exitFullScreenButton.widthAnchor constraintLessThanOrEqualToConstant:exitFullScreenButtonWidth] setActive:YES];
1130 [m_exitFullScreenButton setView:exitFullScreenButton];
1132 touchBar.escapeKeyReplacementItem = m_exitFullScreenButton.get();
1134 touchBar.escapeKeyReplacementItem = nil;
1136 // The rest of the work to update the media function bar only applies to the popover version, so return early.
1140 if (m_playbackControlsManager && m_view == m_view.window.firstResponder && [m_view respondsToSelector:@selector(_web_didAddMediaControlsManager:)])
1141 [m_view _web_didAddMediaControlsManager:m_mediaPlaybackControlsView.get()];
1145 void WebViewImpl::forceRequestCandidatesForTesting()
1147 m_canCreateTouchBars = true;
1153 bool WebViewImpl::shouldRequestCandidates() const
1155 return !m_page->editorState().isInPasswordField && candidateListTouchBarItem().candidateListVisible;
1158 void WebViewImpl::showCandidates(NSArray *candidates, NSString *string, NSRect rectOfTypedString, NSRange selectedRange, NSView *view, void (^completionHandler)(NSTextCheckingResult *acceptedCandidate))
1160 [candidateListTouchBarItem() setCandidates:candidates forSelectedRange:selectedRange inString:string];
1163 void WebViewImpl::setEditableElementIsFocused(bool editableElementIsFocused)
1165 m_editableElementIsFocused = editableElementIsFocused;
1168 // If the editable elements have blurred, then we might need to get rid of the editing function bar.
1169 if (!m_editableElementIsFocused)
1174 } // namespace WebKit
1178 void WebViewImpl::forceRequestCandidatesForTesting()
1182 bool WebViewImpl::shouldRequestCandidates() const
1187 void WebViewImpl::showCandidates(NSArray *candidates, NSString *string, NSRect rectOfTypedString, NSRange selectedRange, NSView *view, void (^completionHandler)(NSTextCheckingResult *acceptedCandidate))
1191 void WebViewImpl::setEditableElementIsFocused(bool editableElementIsFocused)
1193 m_editableElementIsFocused = editableElementIsFocused;
1196 } // namespace WebKit
1197 #endif // HAVE(TOUCH_BAR)
1201 static NSTrackingAreaOptions trackingAreaOptions()
1203 // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
1204 NSTrackingAreaOptions options = NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingInVisibleRect | NSTrackingCursorUpdate;
1205 if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy)
1206 options |= NSTrackingActiveAlways;
1208 options |= NSTrackingActiveInKeyWindow;
1212 WebViewImpl::WebViewImpl(NSView <WebViewImplDelegate> *view, WKWebView *outerWebView, WebProcessPool& processPool, Ref<API::PageConfiguration>&& configuration)
1214 , m_pageClient(std::make_unique<PageClientImpl>(view, outerWebView))
1215 , m_page(processPool.createWebPage(*m_pageClient, WTFMove(configuration)))
1216 , m_weakPtrFactory(this)
1217 , m_needsViewFrameInWindowCoordinates(m_page->preferences().pluginsEnabled())
1218 , m_intrinsicContentSize(CGSizeMake(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric))
1219 , m_layoutStrategy([WKViewLayoutStrategy layoutStrategyWithPage:m_page view:m_view viewImpl:*this mode:kWKLayoutModeViewSize])
1220 , m_undoTarget(adoptNS([[WKEditorUndoTargetObjC alloc] init]))
1221 , m_windowVisibilityObserver(adoptNS([[WKWindowVisibilityObserver alloc] initWithView:view impl:*this]))
1222 , m_accessibilitySettingsObserver(adoptNS([[WKAccessibilitySettingsObserver alloc] initWithImpl:*this]))
1223 , m_primaryTrackingArea(adoptNS([[NSTrackingArea alloc] initWithRect:m_view.frame options:trackingAreaOptions() owner:m_view userInfo:nil]))
1225 static_cast<PageClientImpl&>(*m_pageClient).setImpl(*this);
1227 [NSApp registerServicesMenuSendTypes:PasteboardTypes::forSelection() returnTypes:PasteboardTypes::forEditing()];
1229 [m_view addTrackingArea:m_primaryTrackingArea.get()];
1231 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1233 if (Class gestureClass = NSClassFromString(@"NSImmediateActionGestureRecognizer")) {
1234 m_immediateActionGestureRecognizer = adoptNS([(NSImmediateActionGestureRecognizer *)[gestureClass alloc] init]);
1235 m_immediateActionController = adoptNS([[WKImmediateActionController alloc] initWithPage:m_page view:m_view viewImpl:*this recognizer:m_immediateActionGestureRecognizer.get()]);
1236 [m_immediateActionGestureRecognizer setDelegate:m_immediateActionController.get()];
1237 [m_immediateActionGestureRecognizer setDelaysPrimaryMouseButtonEvents:NO];
1240 m_page->setAddsVisitedLinks(processPool.historyClient().addsVisitedLinks());
1242 m_page->initializeWebPage();
1244 registerDraggedTypes();
1246 m_view.wantsLayer = YES;
1248 // Explicitly set the layer contents placement so AppKit will make sure that our layer has masksToBounds set to YES.
1249 m_view.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
1251 WebProcessPool::statistics().wkViewCount++;
1254 WebViewImpl::~WebViewImpl()
1257 if (m_remoteObjectRegistry) {
1258 m_page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID());
1259 [m_remoteObjectRegistry _invalidate];
1260 m_remoteObjectRegistry = nil;
1264 ASSERT(!m_inSecureInputState);
1267 ASSERT(!m_thumbnailView);
1270 [m_layoutStrategy invalidate];
1272 [m_immediateActionController willDestroyView:m_view];
1275 [m_textTouchBarItemController didDestroyView];
1276 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
1277 [m_mediaTouchBarProvider setPlaybackControlsController:nil];
1278 [m_mediaPlaybackControlsView setPlaybackControlsController:nil];
1284 WebProcessPool::statistics().wkViewCount--;
1288 NSWindow *WebViewImpl::window()
1290 return m_view.window;
1293 void WebViewImpl::processDidExit()
1295 notifyInputContextAboutDiscardedComposition();
1297 if (m_layerHostingView)
1298 setAcceleratedCompositingRootLayer(nil);
1300 updateRemoteAccessibilityRegistration(false);
1302 m_gestureController = nullptr;
1305 void WebViewImpl::pageClosed()
1307 updateRemoteAccessibilityRegistration(false);
1310 void WebViewImpl::didRelaunchProcess()
1312 accessibilityRegisterUIProcessTokens();
1315 void WebViewImpl::setDrawsBackground(bool drawsBackground)
1317 m_page->setDrawsBackground(drawsBackground);
1320 bool WebViewImpl::drawsBackground() const
1322 return m_page->drawsBackground();
1325 bool WebViewImpl::isOpaque() const
1327 return m_page->drawsBackground();
1330 bool WebViewImpl::acceptsFirstResponder()
1335 bool WebViewImpl::becomeFirstResponder()
1337 // If we just became first responder again, there is no need to do anything,
1338 // since resignFirstResponder has correctly detected this situation.
1339 if (m_willBecomeFirstResponderAgain) {
1340 m_willBecomeFirstResponderAgain = false;
1344 NSSelectionDirection direction = [[m_view window] keyViewSelectionDirection];
1346 m_inBecomeFirstResponder = true;
1348 updateSecureInputState();
1349 m_page->activityStateDidChange(WebCore::ActivityState::IsFocused);
1350 // Restore the selection in the editable region if resigning first responder cleared selection.
1351 m_page->restoreSelectionInFocusedEditableElement();
1353 m_inBecomeFirstResponder = false;
1359 if (direction != NSDirectSelection) {
1360 NSEvent *event = [NSApp currentEvent];
1361 NSEvent *keyboardEvent = nil;
1362 if ([event type] == NSEventTypeKeyDown || [event type] == NSEventTypeKeyUp)
1363 keyboardEvent = event;
1364 m_page->setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, false, { }), [](WebKit::CallbackBase::Error) { });
1369 bool WebViewImpl::resignFirstResponder()
1372 // Predict the case where we are losing first responder status only to
1373 // gain it back again. We want resignFirstResponder to do nothing in that case.
1374 id nextResponder = [[m_view window] _newFirstResponderAfterResigning];
1376 // FIXME: This will probably need to change once WKWebView doesn't contain a WKView.
1377 if ([nextResponder isKindOfClass:[WKWebView class]] && m_view.superview == nextResponder) {
1378 m_willBecomeFirstResponderAgain = true;
1383 m_willBecomeFirstResponderAgain = false;
1384 m_inResignFirstResponder = true;
1386 m_page->confirmCompositionAsync();
1388 notifyInputContextAboutDiscardedComposition();
1390 resetSecureInputState();
1392 if (!m_page->maintainsInactiveSelection())
1393 m_page->clearSelection();
1395 m_page->activityStateDidChange(WebCore::ActivityState::IsFocused);
1397 m_inResignFirstResponder = false;
1402 bool WebViewImpl::isFocused() const
1404 if (m_inBecomeFirstResponder)
1406 if (m_inResignFirstResponder)
1408 return m_view.window.firstResponder == m_view;
1411 void WebViewImpl::viewWillStartLiveResize()
1413 m_page->viewWillStartLiveResize();
1415 [m_layoutStrategy willStartLiveResize];
1418 void WebViewImpl::viewDidEndLiveResize()
1420 m_page->viewWillEndLiveResize();
1422 [m_layoutStrategy didEndLiveResize];
1425 void WebViewImpl::renewGState()
1427 if (m_textIndicatorWindow)
1428 dismissContentRelativeChildWindowsWithAnimation(false);
1430 // Update the view frame.
1432 updateWindowAndViewFrames();
1434 updateContentInsetsIfAutomatic();
1437 void WebViewImpl::setFrameSize(CGSize)
1439 [m_layoutStrategy didChangeFrameSize];
1442 void WebViewImpl::disableFrameSizeUpdates()
1444 [m_layoutStrategy disableFrameSizeUpdates];
1447 void WebViewImpl::enableFrameSizeUpdates()
1449 [m_layoutStrategy enableFrameSizeUpdates];
1452 bool WebViewImpl::frameSizeUpdatesDisabled() const
1454 return [m_layoutStrategy frameSizeUpdatesDisabled];
1457 void WebViewImpl::setFrameAndScrollBy(CGRect frame, CGSize offset)
1459 ASSERT(CGSizeEqualToSize(m_resizeScrollOffset, CGSizeZero));
1461 m_resizeScrollOffset = offset;
1462 m_view.frame = NSRectFromCGRect(frame);
1465 void WebViewImpl::updateWindowAndViewFrames()
1467 if (clipsToVisibleRect())
1468 updateViewExposedRect();
1470 if (m_didScheduleWindowAndViewFrameUpdate)
1473 m_didScheduleWindowAndViewFrameUpdate = true;
1475 auto weakThis = createWeakPtr();
1476 dispatch_async(dispatch_get_main_queue(), [weakThis] {
1480 weakThis->m_didScheduleWindowAndViewFrameUpdate = false;
1482 NSRect viewFrameInWindowCoordinates = NSZeroRect;
1483 NSPoint accessibilityPosition = NSZeroPoint;
1485 if (weakThis->m_needsViewFrameInWindowCoordinates)
1486 viewFrameInWindowCoordinates = [weakThis->m_view convertRect:weakThis->m_view.frame toView:nil];
1488 #pragma clang diagnostic push
1489 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1490 if (WebCore::AXObjectCache::accessibilityEnabled())
1491 accessibilityPosition = [[weakThis->m_view accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
1492 #pragma clang diagnostic pop
1494 weakThis->m_page->windowAndViewFramesChanged(viewFrameInWindowCoordinates, accessibilityPosition);
1498 void WebViewImpl::setFixedLayoutSize(CGSize fixedLayoutSize)
1500 m_lastRequestedFixedLayoutSize = fixedLayoutSize;
1502 if (supportsArbitraryLayoutModes())
1503 m_page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
1506 CGSize WebViewImpl::fixedLayoutSize() const
1508 return m_page->fixedLayoutSize();
1511 std::unique_ptr<WebKit::DrawingAreaProxy> WebViewImpl::createDrawingAreaProxy()
1513 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"WebKit2UseRemoteLayerTreeDrawingArea"] boolValue])
1514 return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(m_page);
1516 return std::make_unique<TiledCoreAnimationDrawingAreaProxy>(m_page);
1519 bool WebViewImpl::isUsingUISideCompositing() const
1521 auto* drawingArea = m_page->drawingArea();
1522 return drawingArea && drawingArea->type() == DrawingAreaTypeRemoteLayerTree;
1525 void WebViewImpl::setDrawingAreaSize(CGSize size)
1527 if (!m_page->drawingArea())
1530 m_page->drawingArea()->setSize(WebCore::IntSize(size), WebCore::IntSize(), WebCore::IntSize(m_resizeScrollOffset));
1531 m_resizeScrollOffset = CGSizeZero;
1534 void WebViewImpl::updateLayer()
1536 m_view.layer.backgroundColor = CGColorGetConstantColor(drawsBackground() ? kCGColorWhite : kCGColorClear);
1538 // If asynchronous geometry updates have been sent by forceAsyncDrawingAreaSizeUpdate,
1539 // then subsequent calls to setFrameSize should not result in us waiting for the did
1540 // udpate response if setFrameSize is called.
1541 if (frameSizeUpdatesDisabled())
1544 if (DrawingAreaProxy* drawingArea = m_page->drawingArea())
1545 drawingArea->waitForPossibleGeometryUpdate();
1548 void WebViewImpl::drawRect(CGRect rect)
1550 LOG(Printing, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
1551 m_page->endPrinting();
1554 bool WebViewImpl::canChangeFrameLayout(WebFrameProxy& frame)
1556 // PDF documents are already paginated, so we can't change them to add headers and footers.
1557 return !frame.isDisplayingPDFDocument();
1560 NSPrintOperation *WebViewImpl::printOperationWithPrintInfo(NSPrintInfo *printInfo, WebFrameProxy& frame)
1562 LOG(Printing, "Creating an NSPrintOperation for frame '%s'", frame.url().utf8().data());
1564 // FIXME: If the frame cannot be printed (e.g. if it contains an encrypted PDF that disallows
1565 // printing), this function should return nil.
1566 RetainPtr<WKPrintingView> printingView = adoptNS([[WKPrintingView alloc] initWithFrameProxy:&frame view:m_view]);
1567 // NSPrintOperation takes ownership of the view.
1568 NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get() printInfo:printInfo];
1569 [printOperation setCanSpawnSeparateThread:YES];
1570 [printOperation setJobTitle:frame.title()];
1571 printingView->_printOperation = printOperation;
1572 return printOperation;
1575 void WebViewImpl::setAutomaticallyAdjustsContentInsets(bool automaticallyAdjustsContentInsets)
1577 m_automaticallyAdjustsContentInsets = automaticallyAdjustsContentInsets;
1578 updateContentInsetsIfAutomatic();
1581 void WebViewImpl::updateContentInsetsIfAutomatic()
1583 if (!m_automaticallyAdjustsContentInsets)
1586 NSWindow *window = m_view.window;
1587 if ((window.styleMask & NSWindowStyleMaskFullSizeContentView) && !window.titlebarAppearsTransparent && ![m_view enclosingScrollView]) {
1588 NSRect contentLayoutRect = [m_view convertRect:window.contentLayoutRect fromView:nil];
1589 CGFloat newTopContentInset = NSMaxY(contentLayoutRect) - NSHeight(contentLayoutRect);
1590 if (m_topContentInset != newTopContentInset)
1591 setTopContentInset(newTopContentInset);
1593 setTopContentInset(0);
1596 void WebViewImpl::setTopContentInset(CGFloat contentInset)
1598 m_topContentInset = contentInset;
1600 if (m_didScheduleSetTopContentInset)
1603 m_didScheduleSetTopContentInset = true;
1605 auto weakThis = createWeakPtr();
1606 dispatch_async(dispatch_get_main_queue(), [weakThis] {
1609 weakThis->dispatchSetTopContentInset();
1613 void WebViewImpl::dispatchSetTopContentInset()
1615 if (!m_didScheduleSetTopContentInset)
1618 m_didScheduleSetTopContentInset = false;
1619 m_page->setTopContentInset(m_topContentInset);
1622 void WebViewImpl::prepareContentInRect(CGRect rect)
1624 m_contentPreparationRect = rect;
1625 m_useContentPreparationRectForVisibleRect = true;
1627 updateViewExposedRect();
1630 void WebViewImpl::updateViewExposedRect()
1632 CGRect exposedRect = NSRectToCGRect([m_view visibleRect]);
1634 if (m_useContentPreparationRectForVisibleRect)
1635 exposedRect = CGRectUnion(m_contentPreparationRect, exposedRect);
1637 if (auto drawingArea = m_page->drawingArea())
1638 drawingArea->setViewExposedRect(m_clipsToVisibleRect ? Optional<WebCore::FloatRect>(exposedRect) : Nullopt);
1641 void WebViewImpl::setClipsToVisibleRect(bool clipsToVisibleRect)
1643 m_clipsToVisibleRect = clipsToVisibleRect;
1644 updateViewExposedRect();
1647 void WebViewImpl::setMinimumSizeForAutoLayout(CGSize minimumSizeForAutoLayout)
1649 bool expandsToFit = minimumSizeForAutoLayout.width > 0;
1651 m_page->setMinimumLayoutSize(WebCore::IntSize(minimumSizeForAutoLayout));
1652 m_page->setMainFrameIsScrollable(!expandsToFit);
1654 setClipsToVisibleRect(expandsToFit);
1657 CGSize WebViewImpl::minimumSizeForAutoLayout() const
1659 return m_page->minimumLayoutSize();
1662 void WebViewImpl::setShouldExpandToViewHeightForAutoLayout(bool shouldExpandToViewHeightForAutoLayout)
1664 m_page->setAutoSizingShouldExpandToViewHeight(shouldExpandToViewHeightForAutoLayout);
1667 bool WebViewImpl::shouldExpandToViewHeightForAutoLayout() const
1669 return m_page->autoSizingShouldExpandToViewHeight();
1672 void WebViewImpl::setIntrinsicContentSize(CGSize intrinsicContentSize)
1674 // If the intrinsic content size is less than the minimum layout width, the content flowed to fit,
1675 // so we can report that that dimension is flexible. If not, we need to report our intrinsic width
1676 // so that autolayout will know to provide space for us.
1678 CGSize intrinsicContentSizeAcknowledgingFlexibleWidth = intrinsicContentSize;
1679 if (intrinsicContentSize.width < m_page->minimumLayoutSize().width())
1680 intrinsicContentSizeAcknowledgingFlexibleWidth.width = NSViewNoInstrinsicMetric;
1682 m_intrinsicContentSize = intrinsicContentSizeAcknowledgingFlexibleWidth;
1683 [m_view invalidateIntrinsicContentSize];
1686 CGSize WebViewImpl::intrinsicContentSize() const
1688 return m_intrinsicContentSize;
1691 void WebViewImpl::setViewScale(CGFloat viewScale)
1693 m_lastRequestedViewScale = viewScale;
1695 if (!supportsArbitraryLayoutModes() && viewScale != 1)
1698 if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
1699 [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
1701 m_page->scaleView(viewScale);
1702 [m_layoutStrategy didChangeViewScale];
1705 CGFloat WebViewImpl::viewScale() const
1707 return m_page->viewScaleFactor();
1710 WKLayoutMode WebViewImpl::layoutMode() const
1712 return [m_layoutStrategy layoutMode];
1715 void WebViewImpl::setLayoutMode(WKLayoutMode layoutMode)
1717 m_lastRequestedLayoutMode = layoutMode;
1719 if (!supportsArbitraryLayoutModes() && layoutMode != kWKLayoutModeViewSize)
1722 if (layoutMode == [m_layoutStrategy layoutMode])
1725 [m_layoutStrategy willChangeLayoutStrategy];
1726 m_layoutStrategy = [WKViewLayoutStrategy layoutStrategyWithPage:m_page view:m_view viewImpl:*this mode:layoutMode];
1729 bool WebViewImpl::supportsArbitraryLayoutModes() const
1731 if ([m_fullScreenWindowController isFullScreen])
1734 WebFrameProxy* frame = m_page->mainFrame();
1738 // If we have a plugin document in the main frame, avoid using custom WKLayoutModes
1739 // and fall back to the defaults, because there's a good chance that it won't work (e.g. with PDFPlugin).
1740 if (frame->containsPluginDocument())
1746 void WebViewImpl::updateSupportsArbitraryLayoutModes()
1748 if (!supportsArbitraryLayoutModes()) {
1749 WKLayoutMode oldRequestedLayoutMode = m_lastRequestedLayoutMode;
1750 CGFloat oldRequestedViewScale = m_lastRequestedViewScale;
1751 CGSize oldRequestedFixedLayoutSize = m_lastRequestedFixedLayoutSize;
1753 setLayoutMode(kWKLayoutModeViewSize);
1754 setFixedLayoutSize(CGSizeZero);
1756 // The 'last requested' parameters will have been overwritten by setting them above, but we don't
1757 // want this to count as a request (only changes from the client count), so reset them.
1758 m_lastRequestedLayoutMode = oldRequestedLayoutMode;
1759 m_lastRequestedViewScale = oldRequestedViewScale;
1760 m_lastRequestedFixedLayoutSize = oldRequestedFixedLayoutSize;
1761 } else if (m_lastRequestedLayoutMode != [m_layoutStrategy layoutMode]) {
1762 setViewScale(m_lastRequestedViewScale);
1763 setLayoutMode(m_lastRequestedLayoutMode);
1764 setFixedLayoutSize(m_lastRequestedFixedLayoutSize);
1768 void WebViewImpl::setOverrideDeviceScaleFactor(CGFloat deviceScaleFactor)
1770 m_overrideDeviceScaleFactor = deviceScaleFactor;
1771 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1774 float WebViewImpl::intrinsicDeviceScaleFactor() const
1776 if (m_overrideDeviceScaleFactor)
1777 return m_overrideDeviceScaleFactor;
1778 if (m_targetWindowForMovePreparation)
1779 return m_targetWindowForMovePreparation.backingScaleFactor;
1780 if (NSWindow *window = m_view.window)
1781 return window.backingScaleFactor;
1782 return [NSScreen mainScreen].backingScaleFactor;
1785 void WebViewImpl::windowDidOrderOffScreen()
1787 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible | WebCore::ActivityState::WindowIsActive);
1790 void WebViewImpl::windowDidOrderOnScreen()
1792 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible | WebCore::ActivityState::WindowIsActive);
1795 void WebViewImpl::windowDidBecomeKey(NSWindow *keyWindow)
1797 if (keyWindow == m_view.window || keyWindow == m_view.window.attachedSheet) {
1799 UIGamepadProvider::singleton().viewBecameActive(m_page.get());
1801 updateSecureInputState();
1802 m_page->activityStateDidChange(WebCore::ActivityState::WindowIsActive);
1806 void WebViewImpl::windowDidResignKey(NSWindow *formerKeyWindow)
1808 if (formerKeyWindow == m_view.window || formerKeyWindow == m_view.window.attachedSheet) {
1810 UIGamepadProvider::singleton().viewBecameInactive(m_page.get());
1812 updateSecureInputState();
1813 m_page->activityStateDidChange(WebCore::ActivityState::WindowIsActive);
1817 void WebViewImpl::windowDidMiniaturize()
1819 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1822 void WebViewImpl::windowDidDeminiaturize()
1824 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1827 void WebViewImpl::windowDidMove()
1829 updateWindowAndViewFrames();
1832 void WebViewImpl::windowDidResize()
1834 updateWindowAndViewFrames();
1837 void WebViewImpl::windowDidChangeBackingProperties(CGFloat oldBackingScaleFactor)
1839 CGFloat newBackingScaleFactor = intrinsicDeviceScaleFactor();
1840 if (oldBackingScaleFactor == newBackingScaleFactor)
1843 m_page->setIntrinsicDeviceScaleFactor(newBackingScaleFactor);
1846 void WebViewImpl::windowDidChangeScreen()
1848 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1849 m_page->windowScreenDidChange([[[[window screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue]);
1852 void WebViewImpl::windowDidChangeLayerHosting()
1854 m_page->layerHostingModeDidChange();
1857 void WebViewImpl::windowDidChangeOcclusionState()
1859 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1862 bool WebViewImpl::mightBeginDragWhileInactive()
1864 if (m_view.window.isKeyWindow)
1867 if (m_page->editorState().selectionIsNone || !m_page->editorState().selectionIsRange)
1873 bool WebViewImpl::mightBeginScrollWhileInactive()
1875 // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
1876 if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy)
1882 void WebViewImpl::accessibilitySettingsDidChange()
1884 m_page->accessibilitySettingsDidChange();
1887 bool WebViewImpl::acceptsFirstMouse(NSEvent *event)
1889 if (!mightBeginDragWhileInactive() && !mightBeginScrollWhileInactive())
1892 // There's a chance that responding to this event will run a nested event loop, and
1893 // fetching a new event might release the old one. Retaining and then autoreleasing
1894 // the current event prevents that from causing a problem inside WebKit or AppKit code.
1895 [[event retain] autorelease];
1897 if (![m_view hitTest:event.locationInWindow])
1900 setLastMouseDownEvent(event);
1901 bool result = m_page->acceptsFirstMouse(event.eventNumber, WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
1902 setLastMouseDownEvent(nil);
1906 bool WebViewImpl::shouldDelayWindowOrderingForEvent(NSEvent *event)
1908 if (!mightBeginDragWhileInactive())
1911 // There's a chance that responding to this event will run a nested event loop, and
1912 // fetching a new event might release the old one. Retaining and then autoreleasing
1913 // the current event prevents that from causing a problem inside WebKit or AppKit code.
1914 [[event retain] autorelease];
1916 if (![m_view hitTest:event.locationInWindow])
1919 setLastMouseDownEvent(event);
1920 bool result = m_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
1921 setLastMouseDownEvent(nil);
1925 bool WebViewImpl::windowResizeMouseLocationIsInVisibleScrollerThumb(CGPoint point)
1927 NSPoint localPoint = [m_view convertPoint:NSPointFromCGPoint(point) fromView:nil];
1928 NSRect visibleThumbRect = NSRect(m_page->visibleScrollerThumbRect());
1929 return NSMouseInRect(localPoint, visibleThumbRect, m_view.isFlipped);
1932 void WebViewImpl::viewWillMoveToWindow(NSWindow *window)
1934 // If we're in the middle of preparing to move to a window, we should only be moved to that window.
1935 ASSERT(!m_targetWindowForMovePreparation || (m_targetWindowForMovePreparation == window));
1937 NSWindow *currentWindow = m_view.window;
1938 if (window == currentWindow)
1941 clearAllEditCommands();
1943 NSWindow *stopObservingWindow = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1944 [m_windowVisibilityObserver stopObserving:stopObservingWindow];
1945 [m_windowVisibilityObserver startObserving:window];
1948 void WebViewImpl::viewDidMoveToWindow()
1950 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1953 windowDidChangeScreen();
1955 WebCore::ActivityState::Flags activityStateChanges = WebCore::ActivityState::WindowIsActive | WebCore::ActivityState::IsVisible;
1956 if (m_shouldDeferViewInWindowChanges)
1957 m_viewInWindowChangeWasDeferred = true;
1959 activityStateChanges |= WebCore::ActivityState::IsInWindow;
1960 m_page->activityStateDidChange(activityStateChanges);
1962 updateWindowAndViewFrames();
1964 // FIXME(135509) This call becomes unnecessary once 135509 is fixed; remove.
1965 m_page->layerHostingModeDidChange();
1967 if (!m_flagsChangedEventMonitor) {
1968 auto weakThis = createWeakPtr();
1969 m_flagsChangedEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskFlagsChanged handler:[weakThis] (NSEvent *flagsChangedEvent) {
1971 weakThis->postFakeMouseMovedEventForFlagsChangedEvent(flagsChangedEvent);
1972 return flagsChangedEvent;
1976 accessibilityRegisterUIProcessTokens();
1978 if (m_immediateActionGestureRecognizer && ![[m_view gestureRecognizers] containsObject:m_immediateActionGestureRecognizer.get()] && !m_ignoresNonWheelEvents && m_allowsLinkPreview)
1979 [m_view addGestureRecognizer:m_immediateActionGestureRecognizer.get()];
1981 WebCore::ActivityState::Flags activityStateChanges = WebCore::ActivityState::WindowIsActive | WebCore::ActivityState::IsVisible;
1982 if (m_shouldDeferViewInWindowChanges)
1983 m_viewInWindowChangeWasDeferred = true;
1985 activityStateChanges |= WebCore::ActivityState::IsInWindow;
1986 m_page->activityStateDidChange(activityStateChanges);
1988 [NSEvent removeMonitor:m_flagsChangedEventMonitor];
1989 m_flagsChangedEventMonitor = nil;
1991 dismissContentRelativeChildWindowsWithAnimation(false);
1993 if (m_immediateActionGestureRecognizer) {
1994 // Work around <rdar://problem/22646404> by explicitly cancelling the animation.
1995 cancelImmediateActionAnimation();
1996 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
2000 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
2003 void WebViewImpl::viewDidChangeBackingProperties()
2005 NSColorSpace *colorSpace = m_view.window.colorSpace;
2006 if ([colorSpace isEqualTo:m_colorSpace.get()])
2009 m_colorSpace = nullptr;
2010 if (DrawingAreaProxy *drawingArea = m_page->drawingArea())
2011 drawingArea->colorSpaceDidChange();
2014 void WebViewImpl::viewDidHide()
2016 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
2019 void WebViewImpl::viewDidUnhide()
2021 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
2024 void WebViewImpl::activeSpaceDidChange()
2026 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
2029 NSView *WebViewImpl::hitTest(CGPoint point)
2031 NSView *hitView = [m_view _web_superHitTest:NSPointFromCGPoint(point)];
2032 if (hitView && hitView == m_layerHostingView)
2038 void WebViewImpl::postFakeMouseMovedEventForFlagsChangedEvent(NSEvent *flagsChangedEvent)
2040 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSEventTypeMouseMoved location:flagsChangedEvent.window.mouseLocationOutsideOfEventStream
2041 modifierFlags:flagsChangedEvent.modifierFlags timestamp:flagsChangedEvent.timestamp windowNumber:flagsChangedEvent.windowNumber
2042 context:nullptr eventNumber:0 clickCount:0 pressure:0];
2043 NativeWebMouseEvent webEvent(fakeEvent, m_lastPressureEvent.get(), m_view);
2044 m_page->handleMouseEvent(webEvent);
2047 ColorSpaceData WebViewImpl::colorSpace()
2049 if (!m_colorSpace) {
2050 if (m_targetWindowForMovePreparation)
2051 m_colorSpace = m_targetWindowForMovePreparation.colorSpace;
2052 else if (NSWindow *window = m_view.window)
2053 m_colorSpace = window.colorSpace;
2055 m_colorSpace = [NSScreen mainScreen].colorSpace;
2058 ColorSpaceData colorSpaceData;
2059 colorSpaceData.cgColorSpace = [m_colorSpace CGColorSpace];
2061 return colorSpaceData;
2064 void WebViewImpl::setUnderlayColor(NSColor *underlayColor)
2066 m_page->setUnderlayColor(WebCore::colorFromNSColor(underlayColor));
2069 NSColor *WebViewImpl::underlayColor() const
2071 WebCore::Color webColor = m_page->underlayColor();
2072 if (!webColor.isValid())
2075 return WebCore::nsColor(webColor);
2078 NSColor *WebViewImpl::pageExtendedBackgroundColor() const
2080 WebCore::Color color = m_page->pageExtendedBackgroundColor();
2081 if (!color.isValid())
2084 return WebCore::nsColor(color);
2087 void WebViewImpl::setOverlayScrollbarStyle(WTF::Optional<WebCore::ScrollbarOverlayStyle> scrollbarStyle)
2089 m_page->setOverlayScrollbarStyle(scrollbarStyle);
2092 WTF::Optional<WebCore::ScrollbarOverlayStyle> WebViewImpl::overlayScrollbarStyle() const
2094 return m_page->overlayScrollbarStyle();
2097 void WebViewImpl::beginDeferringViewInWindowChanges()
2099 if (m_shouldDeferViewInWindowChanges) {
2100 NSLog(@"beginDeferringViewInWindowChanges was called while already deferring view-in-window changes!");
2104 m_shouldDeferViewInWindowChanges = true;
2107 void WebViewImpl::endDeferringViewInWindowChanges()
2109 if (!m_shouldDeferViewInWindowChanges) {
2110 NSLog(@"endDeferringViewInWindowChanges was called without beginDeferringViewInWindowChanges!");
2114 m_shouldDeferViewInWindowChanges = false;
2116 if (m_viewInWindowChangeWasDeferred) {
2117 dispatchSetTopContentInset();
2118 m_page->activityStateDidChange(WebCore::ActivityState::IsInWindow);
2119 m_viewInWindowChangeWasDeferred = false;
2123 void WebViewImpl::endDeferringViewInWindowChangesSync()
2125 if (!m_shouldDeferViewInWindowChanges) {
2126 NSLog(@"endDeferringViewInWindowChangesSync was called without beginDeferringViewInWindowChanges!");
2130 m_shouldDeferViewInWindowChanges = false;
2132 if (m_viewInWindowChangeWasDeferred) {
2133 dispatchSetTopContentInset();
2134 m_page->activityStateDidChange(WebCore::ActivityState::IsInWindow);
2135 m_viewInWindowChangeWasDeferred = false;
2139 void WebViewImpl::prepareForMoveToWindow(NSWindow *targetWindow, std::function<void()> completionHandler)
2141 m_shouldDeferViewInWindowChanges = true;
2142 viewWillMoveToWindow(targetWindow);
2143 m_targetWindowForMovePreparation = targetWindow;
2144 viewDidMoveToWindow();
2146 m_shouldDeferViewInWindowChanges = false;
2148 auto weakThis = createWeakPtr();
2149 m_page->installActivityStateChangeCompletionHandler([weakThis, completionHandler]() {
2150 completionHandler();
2155 ASSERT(weakThis->m_view.window == weakThis->m_targetWindowForMovePreparation);
2156 weakThis->m_targetWindowForMovePreparation = nil;
2159 dispatchSetTopContentInset();
2160 m_page->activityStateDidChange(WebCore::ActivityState::IsInWindow, false, WebPageProxy::ActivityStateChangeDispatchMode::Immediate);
2161 m_viewInWindowChangeWasDeferred = false;
2164 void WebViewImpl::updateSecureInputState()
2166 if (![[m_view window] isKeyWindow] || !isFocused()) {
2167 if (m_inSecureInputState) {
2168 DisableSecureEventInput();
2169 m_inSecureInputState = false;
2173 // WKView has a single input context for all editable areas (except for plug-ins).
2174 NSTextInputContext *context = [m_view _web_superInputContext];
2175 bool isInPasswordField = m_page->editorState().isInPasswordField;
2177 if (isInPasswordField) {
2178 if (!m_inSecureInputState)
2179 EnableSecureEventInput();
2180 static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1];
2181 LOG(TextInput, "-> setAllowedInputSourceLocales:romanInputSources");
2182 [context setAllowedInputSourceLocales:romanInputSources];
2184 if (m_inSecureInputState)
2185 DisableSecureEventInput();
2186 LOG(TextInput, "-> setAllowedInputSourceLocales:nil");
2187 [context setAllowedInputSourceLocales:nil];
2189 m_inSecureInputState = isInPasswordField;
2192 void WebViewImpl::resetSecureInputState()
2194 if (m_inSecureInputState) {
2195 DisableSecureEventInput();
2196 m_inSecureInputState = false;
2200 void WebViewImpl::notifyInputContextAboutDiscardedComposition()
2202 // <rdar://problem/9359055>: -discardMarkedText can only be called for active contexts.
2203 // FIXME: We fail to ever notify the input context if something (e.g. a navigation) happens while the window is not key.
2204 // This is not a problem when the window is key, because we discard marked text on resigning first responder.
2205 if (![[m_view window] isKeyWindow] || m_view != [[m_view window] firstResponder])
2208 LOG(TextInput, "-> discardMarkedText");
2210 [[m_view _web_superInputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to.
2213 void WebViewImpl::setPluginComplexTextInputState(PluginComplexTextInputState pluginComplexTextInputState)
2215 m_pluginComplexTextInputState = pluginComplexTextInputState;
2217 if (m_pluginComplexTextInputState != PluginComplexTextInputDisabled)
2220 // Send back an empty string to the plug-in. This will disable text input.
2221 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, String());
2224 void WebViewImpl::setPluginComplexTextInputStateAndIdentifier(PluginComplexTextInputState pluginComplexTextInputState, uint64_t pluginComplexTextInputIdentifier)
2226 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier) {
2227 // We're asked to update the state for a plug-in that doesn't have focus.
2231 setPluginComplexTextInputState(pluginComplexTextInputState);
2234 void WebViewImpl::disableComplexTextInputIfNecessary()
2236 if (!m_pluginComplexTextInputIdentifier)
2239 if (m_pluginComplexTextInputState != PluginComplexTextInputEnabled)
2242 // Check if the text input window has been dismissed.
2243 if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
2244 setPluginComplexTextInputState(PluginComplexTextInputDisabled);
2247 bool WebViewImpl::handlePluginComplexTextInputKeyDown(NSEvent *event)
2249 ASSERT(m_pluginComplexTextInputIdentifier);
2250 ASSERT(m_pluginComplexTextInputState != PluginComplexTextInputDisabled);
2252 BOOL usingLegacyCocoaTextInput = m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
2254 NSString *string = nil;
2255 BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string];
2258 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, string);
2260 if (!usingLegacyCocoaTextInput)
2261 m_pluginComplexTextInputState = PluginComplexTextInputDisabled;
2264 return didHandleEvent;
2267 bool WebViewImpl::tryHandlePluginComplexTextInputKeyDown(NSEvent *event)
2269 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
2272 // Check if the text input window has been dismissed and let the plug-in process know.
2273 // This is only valid with the updated Cocoa text input spec.
2274 disableComplexTextInputIfNecessary();
2276 // Try feeding the keyboard event directly to the plug-in.
2277 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
2278 return handlePluginComplexTextInputKeyDown(event);
2283 void WebViewImpl::pluginFocusOrWindowFocusChanged(bool pluginHasFocusAndWindowHasFocus, uint64_t pluginComplexTextInputIdentifier)
2285 BOOL inputSourceChanged = m_pluginComplexTextInputIdentifier;
2287 if (pluginHasFocusAndWindowHasFocus) {
2288 // Check if we're already allowing text input for this plug-in.
2289 if (pluginComplexTextInputIdentifier == m_pluginComplexTextInputIdentifier)
2292 m_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier;
2295 // Check if we got a request to unfocus a plug-in that isn't focused.
2296 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier)
2299 m_pluginComplexTextInputIdentifier = 0;
2302 if (inputSourceChanged) {
2303 // The input source changed; discard any entered text.
2304 [[WKTextInputWindowController sharedTextInputWindowController] unmarkText];
2307 // This will force the current input context to be updated to its correct value.
2308 [NSApp updateWindows];
2311 bool WebViewImpl::tryPostProcessPluginComplexTextInputKeyDown(NSEvent *event)
2313 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
2316 // In the legacy text input model, the event has already been sent to the input method.
2317 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
2320 return handlePluginComplexTextInputKeyDown(event);
2323 void WebViewImpl::handleAcceptedAlternativeText(const String& acceptedAlternative)
2325 m_page->handleAlternativeTextUIResult(acceptedAlternative);
2329 NSInteger WebViewImpl::spellCheckerDocumentTag()
2331 if (!m_spellCheckerDocumentTag)
2332 m_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
2333 return m_spellCheckerDocumentTag.value();
2336 void WebViewImpl::pressureChangeWithEvent(NSEvent *event)
2338 #if defined(__LP64__)
2339 if (event == m_lastPressureEvent)
2342 if (m_ignoresNonWheelEvents)
2345 if (event.phase != NSEventPhaseChanged && event.phase != NSEventPhaseBegan && event.phase != NSEventPhaseEnded)
2348 NativeWebMouseEvent webEvent(event, m_lastPressureEvent.get(), m_view);
2349 m_page->handleMouseEvent(webEvent);
2351 m_lastPressureEvent = event;
2355 #if ENABLE(FULLSCREEN_API)
2356 bool WebViewImpl::hasFullScreenWindowController() const
2358 return !!m_fullScreenWindowController;
2361 WKFullScreenWindowController *WebViewImpl::fullScreenWindowController()
2363 if (!m_fullScreenWindowController)
2364 m_fullScreenWindowController = adoptNS([[WKFullScreenWindowController alloc] initWithWindow:createFullScreenWindow() webView:m_view page:m_page]);
2366 return m_fullScreenWindowController.get();
2369 void WebViewImpl::closeFullScreenWindowController()
2371 if (!m_fullScreenWindowController)
2374 [m_fullScreenWindowController close];
2375 m_fullScreenWindowController = nullptr;
2379 NSView *WebViewImpl::fullScreenPlaceholderView()
2381 #if ENABLE(FULLSCREEN_API)
2382 if (m_fullScreenWindowController && [m_fullScreenWindowController isFullScreen])
2383 return [m_fullScreenWindowController webViewPlaceholder];
2388 NSWindow *WebViewImpl::createFullScreenWindow()
2390 #if ENABLE(FULLSCREEN_API)
2391 return [[[WebCoreFullScreenWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] styleMask:(NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable) backing:NSBackingStoreBuffered defer:NO] autorelease];
2397 bool WebViewImpl::isEditable() const
2399 return m_page->isEditable();
2402 typedef HashMap<SEL, String> SelectorNameMap;
2404 // Map selectors into Editor command names.
2405 // This is not needed for any selectors that have the same name as the Editor command.
2406 static const SelectorNameMap& selectorExceptionMap()
2408 static NeverDestroyed<SelectorNameMap> map;
2410 struct SelectorAndCommandName {
2412 ASCIILiteral commandName;
2415 static const SelectorAndCommandName names[] = {
2416 { @selector(insertNewlineIgnoringFieldEditor:), ASCIILiteral("InsertNewline") },
2417 { @selector(insertParagraphSeparator:), ASCIILiteral("InsertNewline") },
2418 { @selector(insertTabIgnoringFieldEditor:), ASCIILiteral("InsertTab") },
2419 { @selector(pageDown:), ASCIILiteral("MovePageDown") },
2420 { @selector(pageDownAndModifySelection:), ASCIILiteral("MovePageDownAndModifySelection") },
2421 { @selector(pageUp:), ASCIILiteral("MovePageUp") },
2422 { @selector(pageUpAndModifySelection:), ASCIILiteral("MovePageUpAndModifySelection") },
2423 { @selector(scrollPageDown:), ASCIILiteral("ScrollPageForward") },
2424 { @selector(scrollPageUp:), ASCIILiteral("ScrollPageBackward") }
2427 for (auto& name : names)
2428 map.get().add(name.selector, name.commandName);
2433 static String commandNameForSelector(SEL selector)
2435 // Check the exception map first.
2436 static const SelectorNameMap& exceptionMap = selectorExceptionMap();
2437 SelectorNameMap::const_iterator it = exceptionMap.find(selector);
2438 if (it != exceptionMap.end())
2441 // Remove the trailing colon.
2442 // No need to capitalize the command name since Editor command names are
2443 // not case sensitive.
2444 const char* selectorName = sel_getName(selector);
2445 size_t selectorNameLength = strlen(selectorName);
2446 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2448 return String(selectorName, selectorNameLength - 1);
2451 bool WebViewImpl::executeSavedCommandBySelector(SEL selector)
2453 LOG(TextInput, "Executing previously saved command %s", sel_getName(selector));
2454 // The sink does two things: 1) Tells us if the responder went unhandled, and
2455 // 2) prevents any NSBeep; we don't ever want to beep here.
2456 RetainPtr<WKResponderChainSink> sink = adoptNS([[WKResponderChainSink alloc] initWithResponderChain:m_view]);
2457 [m_view _web_superDoCommandBySelector:selector];
2459 return ![sink didReceiveUnhandledCommand];
2462 void WebViewImpl::executeEditCommandForSelector(SEL selector, const String& argument)
2464 m_page->executeEditCommand(commandNameForSelector(selector), argument);
2467 void WebViewImpl::registerEditCommand(RefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo)
2469 RefPtr<WebEditCommandProxy> command = prpCommand;
2471 RetainPtr<WKEditCommandObjC> commandObjC = adoptNS([[WKEditCommandObjC alloc] initWithWebEditCommandProxy:command]);
2472 String actionName = WebEditCommandProxy::nameForEditAction(command->editAction());
2474 NSUndoManager *undoManager = [m_view undoManager];
2475 [undoManager registerUndoWithTarget:m_undoTarget.get() selector:((undoOrRedo == WebPageProxy::Undo) ? @selector(undoEditing:) : @selector(redoEditing:)) object:commandObjC.get()];
2476 if (!actionName.isEmpty())
2477 [undoManager setActionName:(NSString *)actionName];
2480 void WebViewImpl::clearAllEditCommands()
2482 [[m_view undoManager] removeAllActionsWithTarget:m_undoTarget.get()];
2485 bool WebViewImpl::writeSelectionToPasteboard(NSPasteboard *pasteboard, NSArray *types)
2487 size_t numTypes = types.count;
2488 [pasteboard declareTypes:types owner:nil];
2489 for (size_t i = 0; i < numTypes; ++i) {
2490 if ([[types objectAtIndex:i] isEqualTo:NSStringPboardType])
2491 [pasteboard setString:m_page->stringSelectionForPasteboard() forType:NSStringPboardType];
2493 RefPtr<WebCore::SharedBuffer> buffer = m_page->dataSelectionForPasteboard([types objectAtIndex:i]);
2494 [pasteboard setData:buffer ? buffer->createNSData().get() : nil forType:[types objectAtIndex:i]];
2500 bool WebViewImpl::readSelectionFromPasteboard(NSPasteboard *pasteboard)
2502 return m_page->readSelectionFromPasteboard([pasteboard name]);
2505 id WebViewImpl::validRequestorForSendAndReturnTypes(NSString *sendType, NSString *returnType)
2507 EditorState editorState = m_page->editorState();
2508 bool isValidSendType = false;
2510 if (sendType && !editorState.selectionIsNone) {
2511 if (editorState.isInPlugin)
2512 isValidSendType = [sendType isEqualToString:NSStringPboardType];
2514 isValidSendType = [PasteboardTypes::forSelection() containsObject:sendType];
2517 bool isValidReturnType = false;
2519 isValidReturnType = true;
2520 else if ([PasteboardTypes::forEditing() containsObject:returnType] && editorState.isContentEditable) {
2521 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts.
2522 isValidReturnType = editorState.isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType];
2524 if (isValidSendType && isValidReturnType)
2526 return [[m_view nextResponder] validRequestorForSendType:sendType returnType:returnType];
2529 void WebViewImpl::centerSelectionInVisibleArea()
2531 m_page->centerSelectionInVisibleArea();
2534 void WebViewImpl::selectionDidChange()
2536 updateFontPanelIfNeeded();
2537 if (!m_isHandlingAcceptedCandidate)
2538 m_softSpaceRange = NSMakeRange(NSNotFound, 0);
2541 if (!m_page->editorState().isMissingPostLayoutData)
2542 requestCandidatesForSelectionIfNeeded();
2546 void WebViewImpl::startObservingFontPanel()
2548 [m_windowVisibilityObserver startObservingFontPanel];
2551 void WebViewImpl::updateFontPanelIfNeeded()
2553 const EditorState& editorState = m_page->editorState();
2554 if (editorState.selectionIsNone || !editorState.isContentEditable)
2556 if ([NSFontPanel sharedFontPanelExists] && [[NSFontPanel sharedFontPanel] isVisible]) {
2557 m_page->fontAtSelection([](const String& fontName, double fontSize, bool selectionHasMultipleFonts, WebKit::CallbackBase::Error error) {
2558 NSFont *font = [NSFont fontWithName:fontName size:fontSize];
2560 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:selectionHasMultipleFonts];
2565 void WebViewImpl::changeFontFromFontPanel()
2567 NSFontManager *fontManager = [NSFontManager sharedFontManager];
2568 NSFont *font = [fontManager convertFont:fontManager.selectedFont];
2571 m_page->setFont(font.familyName, font.pointSize, font.fontDescriptor.symbolicTraits);
2574 static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item)
2576 if (![(NSObject *)item isKindOfClass:[NSMenuItem class]])
2578 return (NSMenuItem *)item;
2581 static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item)
2583 if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]])
2585 return (NSToolbarItem *)item;
2588 bool WebViewImpl::validateUserInterfaceItem(id <NSValidatedUserInterfaceItem> item)
2590 SEL action = [item action];
2592 if (action == @selector(showGuessPanel:)) {
2593 if (NSMenuItem *menuItem = WebKit::menuItem(item))
2594 [menuItem setTitle:WebCore::contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])];
2595 return m_page->editorState().isContentEditable;
2598 if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:))
2599 return m_page->editorState().isContentEditable;
2601 if (action == @selector(toggleContinuousSpellChecking:)) {
2602 bool enabled = TextChecker::isContinuousSpellCheckingAllowed();
2603 bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled;
2604 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2608 if (action == @selector(toggleGrammarChecking:)) {
2609 bool checked = TextChecker::state().isGrammarCheckingEnabled;
2610 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2614 if (action == @selector(toggleAutomaticSpellingCorrection:)) {
2615 bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled;
2616 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2617 return m_page->editorState().isContentEditable;
2620 if (action == @selector(orderFrontSubstitutionsPanel:)) {
2621 if (NSMenuItem *menuItem = WebKit::menuItem(item))
2622 [menuItem setTitle:WebCore::contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])];
2623 return m_page->editorState().isContentEditable;
2626 if (action == @selector(toggleSmartInsertDelete:)) {
2627 bool checked = m_page->isSmartInsertDeleteEnabled();
2628 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2629 return m_page->editorState().isContentEditable;
2632 if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
2633 bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
2634 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2635 return m_page->editorState().isContentEditable;
2638 if (action == @selector(toggleAutomaticDashSubstitution:)) {
2639 bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled;
2640 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2641 return m_page->editorState().isContentEditable;
2644 if (action == @selector(toggleAutomaticLinkDetection:)) {
2645 bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled;
2646 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2647 return m_page->editorState().isContentEditable;
2650 if (action == @selector(toggleAutomaticTextReplacement:)) {
2651 bool checked = TextChecker::state().isAutomaticTextReplacementEnabled;
2652 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2653 return m_page->editorState().isContentEditable;
2656 if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:))
2657 return m_page->editorState().selectionIsRange && m_page->editorState().isContentEditable;
2659 if (action == @selector(stopSpeaking:))
2660 return [NSApp isSpeaking];
2662 // The centerSelectionInVisibleArea: selector is enabled if there's a selection range or if there's an insertion point in an editable area.
2663 if (action == @selector(centerSelectionInVisibleArea:))
2664 return m_page->editorState().selectionIsRange || (m_page->editorState().isContentEditable && !m_page->editorState().selectionIsNone);
2666 // Next, handle editor commands. Start by returning true for anything that is not an editor command.
2667 // Returning true is the default thing to do in an AppKit validate method for any selector that is not recognized.
2668 String commandName = commandNameForSelector([item action]);
2669 if (!WebCore::Editor::commandIsSupportedFromMenuOrKeyBinding(commandName))
2672 // Add this item to the vector of items for a given command that are awaiting validation.
2673 ValidationMap::AddResult addResult = m_validationMap.add(commandName, ValidationVector());
2674 addResult.iterator->value.append(item);
2675 if (addResult.isNewEntry) {
2676 // If we are not already awaiting validation for this command, start the asynchronous validation process.
2677 // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time
2678 // we asked for the same command; there is no guarantee the answer is still valid.
2679 auto weakThis = createWeakPtr();
2680 m_page->validateCommand(commandName, [weakThis](const String& commandName, bool isEnabled, int32_t state, WebKit::CallbackBase::Error error) {
2684 // If the process exits before the command can be validated, we'll be called back with an error.
2685 if (error != WebKit::CallbackBase::Error::None)
2688 weakThis->setUserInterfaceItemState(commandName, isEnabled, state);
2692 // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called.
2693 // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment.
2694 // But returning NO here would be worse; that would make keyboard commands such as command-C fail.
2698 void WebViewImpl::setUserInterfaceItemState(NSString *commandName, bool enabled, int state)
2700 ValidationVector items = m_validationMap.take(commandName);
2701 for (auto& item : items) {
2702 [menuItem(item.get()) setState:state];
2703 [menuItem(item.get()) setEnabled:enabled];
2704 [toolbarItem(item.get()) setEnabled:enabled];
2705 // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled.
2709 void WebViewImpl::startSpeaking()
2711 m_page->getSelectionOrContentsAsString([](const String& string, WebKit::CallbackBase::Error error) {
2712 if (error != WebKit::CallbackBase::Error::None)
2717 [NSApp speakString:string];
2721 void WebViewImpl::stopSpeaking(id sender)
2723 [NSApp stopSpeaking:sender];
2726 void WebViewImpl::showGuessPanel(id sender)
2728 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2730 LOG_ERROR("No NSSpellChecker");
2734 NSPanel *spellingPanel = [checker spellingPanel];
2735 if ([spellingPanel isVisible]) {
2736 [spellingPanel orderOut:sender];
2740 m_page->advanceToNextMisspelling(true);
2741 [spellingPanel orderFront:sender];
2744 void WebViewImpl::checkSpelling()
2746 m_page->advanceToNextMisspelling(false);
2749 void WebViewImpl::changeSpelling(id sender)
2751 NSString *word = [[sender selectedCell] stringValue];
2753 m_page->changeSpellingToWord(word);
2756 void WebViewImpl::toggleContinuousSpellChecking()
2758 bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled;
2759 TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled);
2761 m_page->process().updateTextCheckerState();
2764 bool WebViewImpl::isGrammarCheckingEnabled()
2766 return TextChecker::state().isGrammarCheckingEnabled;
2769 void WebViewImpl::setGrammarCheckingEnabled(bool flag)
2771 if (static_cast<bool>(flag) == TextChecker::state().isGrammarCheckingEnabled)
2774 TextChecker::setGrammarCheckingEnabled(flag);
2775 m_page->process().updateTextCheckerState();
2778 void WebViewImpl::toggleGrammarChecking()
2780 bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled;
2781 TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled);
2783 m_page->process().updateTextCheckerState();
2786 void WebViewImpl::toggleAutomaticSpellingCorrection()
2788 TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled);
2790 m_page->process().updateTextCheckerState();
2793 void WebViewImpl::orderFrontSubstitutionsPanel(id sender)
2795 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2797 LOG_ERROR("No NSSpellChecker");
2801 NSPanel *substitutionsPanel = [checker substitutionsPanel];
2802 if ([substitutionsPanel isVisible]) {
2803 [substitutionsPanel orderOut:sender];
2806 [substitutionsPanel orderFront:sender];
2809 void WebViewImpl::toggleSmartInsertDelete()
2811 m_page->setSmartInsertDeleteEnabled(!m_page->isSmartInsertDeleteEnabled());
2814 bool WebViewImpl::isAutomaticQuoteSubstitutionEnabled()
2816 return TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
2819 void WebViewImpl::setAutomaticQuoteSubstitutionEnabled(bool flag)
2821 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled)
2824 TextChecker::setAutomaticQuoteSubstitutionEnabled(flag);
2825 m_page->process().updateTextCheckerState();
2828 void WebViewImpl::toggleAutomaticQuoteSubstitution()
2830 TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled);
2831 m_page->process().updateTextCheckerState();
2834 bool WebViewImpl::isAutomaticDashSubstitutionEnabled()
2836 return TextChecker::state().isAutomaticDashSubstitutionEnabled;
2839 void WebViewImpl::setAutomaticDashSubstitutionEnabled(bool flag)
2841 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled)
2844 TextChecker::setAutomaticDashSubstitutionEnabled(flag);
2845 m_page->process().updateTextCheckerState();
2848 void WebViewImpl::toggleAutomaticDashSubstitution()
2850 TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled);
2851 m_page->process().updateTextCheckerState();
2854 bool WebViewImpl::isAutomaticLinkDetectionEnabled()
2856 return TextChecker::state().isAutomaticLinkDetectionEnabled;
2859 void WebViewImpl::setAutomaticLinkDetectionEnabled(bool flag)
2861 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled)
2864 TextChecker::setAutomaticLinkDetectionEnabled(flag);
2865 m_page->process().updateTextCheckerState();
2868 void WebViewImpl::toggleAutomaticLinkDetection()
2870 TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled);
2871 m_page->process().updateTextCheckerState();
2874 bool WebViewImpl::isAutomaticTextReplacementEnabled()
2876 return TextChecker::state().isAutomaticTextReplacementEnabled;
2879 void WebViewImpl::setAutomaticTextReplacementEnabled(bool flag)
2881 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled)
2884 TextChecker::setAutomaticTextReplacementEnabled(flag);
2885 m_page->process().updateTextCheckerState();
2888 void WebViewImpl::toggleAutomaticTextReplacement()
2890 TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled);
2891 m_page->process().updateTextCheckerState();
2894 void WebViewImpl::uppercaseWord()
2896 m_page->uppercaseWord();
2899 void WebViewImpl::lowercaseWord()
2901 m_page->lowercaseWord();
2904 void WebViewImpl::capitalizeWord()
2906 m_page->capitalizeWord();
2909 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
2910 void WebViewImpl::requestCandidatesForSelectionIfNeeded()
2912 if (!shouldRequestCandidates())
2915 const EditorState& editorState = m_page->editorState();
2916 if (!editorState.isContentEditable)
2919 if (editorState.isMissingPostLayoutData)
2922 auto& postLayoutData = editorState.postLayoutData();
2923 m_lastStringForCandidateRequest = postLayoutData.stringForCandidateRequest;
2925 #if HAVE(ADVANCED_SPELL_CHECKING)
2926 NSRange selectedRange = NSMakeRange(postLayoutData.candidateRequestStartPosition, postLayoutData.selectedTextLength);
2927 NSTextCheckingTypes checkingTypes = NSTextCheckingTypeSpelling | NSTextCheckingTypeReplacement | NSTextCheckingTypeCorrection;
2928 auto weakThis = createWeakPtr();
2929 m_lastCandidateRequestSequenceNumber = [[NSSpellChecker sharedSpellChecker] requestCandidatesForSelectedRange:selectedRange inString:postLayoutData.paragraphContextForCandidateRequest types:checkingTypes options:nil inSpellDocumentWithTag:spellCheckerDocumentTag() completionHandler:[weakThis](NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates) {
2930 dispatch_async(dispatch_get_main_queue(), ^{
2933 weakThis->handleRequestedCandidates(sequenceNumber, candidates);
2936 #endif // HAVE(ADVANCED_SPELL_CHECKING)
2939 void WebViewImpl::handleRequestedCandidates(NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates)
2941 if (!shouldRequestCandidates())
2944 if (m_lastCandidateRequestSequenceNumber != sequenceNumber)
2947 const EditorState& editorState = m_page->editorState();
2948 if (!editorState.isContentEditable)
2951 // FIXME: It's pretty lame that we have to depend on the most recent EditorState having post layout data,
2952 // and that we just bail if it is missing.
2953 if (editorState.isMissingPostLayoutData)
2956 auto& postLayoutData = editorState.postLayoutData();
2957 if (m_lastStringForCandidateRequest != postLayoutData.stringForCandidateRequest)
2960 NSRange selectedRange = NSMakeRange(postLayoutData.candidateRequestStartPosition, postLayoutData.selectedTextLength);
2961 auto weakThis = createWeakPtr();
2962 showCandidates(candidates, postLayoutData.paragraphContextForCandidateRequest, postLayoutData.selectionClipRect, selectedRange, m_view, [weakThis](NSTextCheckingResult *acceptedCandidate) {
2963 dispatch_async(dispatch_get_main_queue(), ^{
2966 weakThis->handleAcceptedCandidate(acceptedCandidate);
2971 static WebCore::TextCheckingResult textCheckingResultFromNSTextCheckingResult(NSTextCheckingResult *nsResult)
2973 WebCore::TextCheckingResult result;
2975 // FIXME: Right now we only request candidates for spelling, replacement, and correction, but we plan to
2976 // support more types, and we will have to update this at that time.
2977 switch ([nsResult resultType]) {
2978 case NSTextCheckingTypeSpelling:
2979 result.type = WebCore::TextCheckingTypeSpelling;
2981 case NSTextCheckingTypeReplacement:
2982 result.type = WebCore::TextCheckingTypeReplacement;
2984 case NSTextCheckingTypeCorrection:
2985 result.type = WebCore::TextCheckingTypeCorrection;
2988 result.type = WebCore::TextCheckingTypeNone;
2991 NSRange resultRange = [nsResult range];
2992 result.location = resultRange.location;
2993 result.length = resultRange.length;
2994 result.replacement = [nsResult replacementString];
2999 void WebViewImpl::handleAcceptedCandidate(NSTextCheckingResult *acceptedCandidate)
3001 const EditorState& editorState = m_page->editorState();
3002 if (!editorState.isContentEditable)
3005 // FIXME: It's pretty lame that we have to depend on the most recent EditorState having post layout data,
3006 // and that we just bail if it is missing.
3007 if (editorState.isMissingPostLayoutData)
3010 auto& postLayoutData = editorState.postLayoutData();
3011 if (m_lastStringForCandidateRequest != postLayoutData.stringForCandidateRequest)
3014 m_isHandlingAcceptedCandidate = true;
3015 NSRange range = [acceptedCandidate range];
3016 if (acceptedCandidate.replacementString && [acceptedCandidate.replacementString length] > 0) {
3017 NSRange replacedRange = NSMakeRange(range.location, [acceptedCandidate.replacementString length]);
3018 NSRange softSpaceRange = NSMakeRange(NSMaxRange(replacedRange) - 1, 1);
3019 if ([acceptedCandidate.replacementString hasSuffix:@" "])
3020 m_softSpaceRange = softSpaceRange;
3023 m_page->handleAcceptedCandidate(textCheckingResultFromNSTextCheckingResult(acceptedCandidate));
3025 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
3027 void WebViewImpl::preferencesDidChange()
3029 BOOL needsViewFrameInWindowCoordinates = m_page->preferences().pluginsEnabled();
3031 if (!!needsViewFrameInWindowCoordinates == !!m_needsViewFrameInWindowCoordinates)
3034 m_needsViewFrameInWindowCoordinates = needsViewFrameInWindowCoordinates;
3036 updateWindowAndViewFrames();
3039 void WebViewImpl::setTextIndicator(WebCore::TextIndicator& textIndicator, WebCore::TextIndicatorWindowLifetime lifetime)
3041 if (!m_textIndicatorWindow)
3042 m_textIndicatorWindow = std::make_unique<WebCore::TextIndicatorWindow>(m_view);
3044 NSRect textBoundingRectInScreenCoordinates = [m_view.window convertRectToScreen:[m_view convertRect:textIndicator.textBoundingRectInRootViewCoordinates() toView:nil]];
3045 m_textIndicatorWindow->setTextIndicator(textIndicator, NSRectToCGRect(textBoundingRectInScreenCoordinates), lifetime);
3048 void WebViewImpl::clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation animation)
3050 if (m_textIndicatorWindow)
3051 m_textIndicatorWindow->clearTextIndicator(animation);
3052 m_textIndicatorWindow = nullptr;
3055 void WebViewImpl::setTextIndicatorAnimationProgress(float progress)
3057 if (m_textIndicatorWindow)
3058 m_textIndicatorWindow->setAnimationProgress(progress);
3061 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimation(bool animate)
3063 [m_view _web_dismissContentRelativeChildWindowsWithAnimation:animate];
3066 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimationFromViewOnly(bool animate)
3068 // Calling _clearTextIndicatorWithAnimation here will win out over the animated clear in dismissContentRelativeChildWindowsFromViewOnly.
3069 // We can't invert these because clients can override (and have overridden) _dismissContentRelativeChildWindows, so it needs to be called.
3070 // For this same reason, this can't be moved to WebViewImpl without care.
3071 clearTextIndicatorWithAnimation(animate ? WebCore::TextIndicatorWindowDismissalAnimation::FadeOut : WebCore::TextIndicatorWindowDismissalAnimation::None);
3072 [m_view _web_dismissContentRelativeChildWindows];
3075 void WebViewImpl::dismissContentRelativeChildWindowsFromViewOnly()
3077 bool hasActiveImmediateAction = false;
3078 hasActiveImmediateAction = [m_immediateActionController hasActiveImmediateAction];
3080 // FIXME: We don't know which panel we are dismissing, it may not even be in the current page (see <rdar://problem/13875766>).
3081 if (m_view.window.isKeyWindow || hasActiveImmediateAction) {
3082 WebCore::DictionaryLookup::hidePopup();
3084 if (DataDetectorsLibrary()) {
3085 DDActionsManager *actionsManager = [getDDActionsManagerClass() sharedManager];
3086 if ([actionsManager respondsToSelector:@selector(requestBubbleClosureUnanchorOnFailure:)])
3087 [actionsManager requestBubbleClosureUnanchorOnFailure:YES];
3091 clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::FadeOut);
3093 [m_immediateActionController dismissContentRelativeChildWindows];
3095 m_pageClient->dismissCorrectionPanel(WebCore::ReasonForDismissingAlternativeTextIgnored);
3098 void WebViewImpl::hideWordDefinitionWindow()
3100 WebCore::DictionaryLookup::hidePopup();
3103 void WebViewImpl::quickLookWithEvent(NSEvent *event)
3105 if (ignoresNonWheelEvents())
3108 if (m_immediateActionGestureRecognizer) {
3109 [m_view _web_superQuickLookWithEvent:event];
3113 NSPoint locationInViewCoordinates = [m_view convertPoint:[event locationInWindow] fromView:nil];
3114 m_page->performDictionaryLookupAtLocation(WebCore::FloatPoint(locationInViewCoordinates));
3117 void WebViewImpl::prepareForDictionaryLookup()
3119 if (m_didRegisterForLookupPopoverCloseNotifications)
3122 m_didRegisterForLookupPopoverCloseNotifications = true;
3124 [m_windowVisibilityObserver startObservingLookupDismissal];
3127 void WebViewImpl::setAllowsLinkPreview(bool allowsLinkPreview)
3129 if (m_allowsLinkPreview == allowsLinkPreview)
3132 m_allowsLinkPreview = allowsLinkPreview;
3134 if (!allowsLinkPreview)
3135 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
3136 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get())
3137 [m_view addGestureRecognizer:immediateActionRecognizer];
3140 void* WebViewImpl::immediateActionAnimationControllerForHitTestResult(API::HitTestResult* hitTestResult, uint32_t type, API::Object* userData)
3142 return [m_view _web_immediateActionAnimationControllerForHitTestResultInternal:hitTestResult withType:type userData:userData];
3145 void WebViewImpl::didPerformImmediateActionHitTest(const WebHitTestResultData& result, bool contentPreventsDefault, API::Object* userData)
3147 [m_immediateActionController didPerformImmediateActionHitTest:result contentPreventsDefault:contentPreventsDefault userData:userData];
3150 void WebViewImpl::prepareForImmediateActionAnimation()
3152 [m_view _web_prepareForImmediateActionAnimation];
3155 void WebViewImpl::cancelImmediateActionAnimation()
3157 [m_view _web_cancelImmediateActionAnimation];
3160 void WebViewImpl::completeImmediateActionAnimation()
3162 [m_view _web_completeImmediateActionAnimation];
3165 void WebViewImpl::didChangeContentSize(CGSize newSize)
3167 [m_view _web_didChangeContentSize:NSSizeFromCGSize(newSize)];
3170 void WebViewImpl::didHandleAcceptedCandidate()
3172 m_isHandlingAcceptedCandidate = false;
3174 [m_view _didHandleAcceptedCandidate];
3177 void WebViewImpl::videoControlsManagerDidChange()
3184 void WebViewImpl::setIgnoresNonWheelEvents(bool ignoresNonWheelEvents)
3186 if (m_ignoresNonWheelEvents == ignoresNonWheelEvents)
3189 m_ignoresNonWheelEvents = ignoresNonWheelEvents;
3190 m_page->setShouldDispatchFakeMouseMoveEvents(!ignoresNonWheelEvents);
3192 if (ignoresNonWheelEvents)
3193 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
3194 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get()) {
3195 if (m_allowsLinkPreview)
3196 [m_view addGestureRecognizer:immediateActionRecognizer];
3200 void WebViewImpl::setIgnoresAllEvents(bool ignoresAllEvents)
3202 m_ignoresAllEvents = ignoresAllEvents;
3203 setIgnoresNonWheelEvents(ignoresAllEvents);
3206 void WebViewImpl::setIgnoresMouseDraggedEvents(bool ignoresMouseDraggedEvents)
3208 m_ignoresMouseDraggedEvents = ignoresMouseDraggedEvents;
3211 void WebViewImpl::setAccessibilityWebProcessToken(NSData *data)
3213 m_remoteAccessibilityChild = WKAXRemoteElementForToken(data);
3214 updateRemoteAccessibilityRegistration(true);
3217 void WebViewImpl::updateRemoteAccessibilityRegistration(bool registerProcess)
3219 // When the tree is connected/disconnected, the remote accessibility registration
3220 // needs to be updated with the pid of the remote process. If the process is going
3221 // away, that information is not present in WebProcess
3223 if (registerProcess)
3224 pid = m_page->process().processIdentifier();
3225 else if (!registerProcess) {
3226 pid = WKAXRemoteProcessIdentifier(m_remoteAccessibilityChild.get());
3227 m_remoteAccessibilityChild = nil;
3230 WKAXRegisterRemoteProcess(registerProcess, pid);
3233 void WebViewImpl::accessibilityRegisterUIProcessTokens()
3235 // Initialize remote accessibility when the window connection has been established.
3236 NSData *remoteElementToken = WKAXRemoteTokenForElement(m_view);
3237 NSData *remoteWindowToken = WKAXRemoteTokenForElement(m_view.window);
3238 IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
3239 IPC::DataReference windowToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]);
3240 m_page->registerUIProcessAccessibilityTokens(elementToken, windowToken);
3243 id WebViewImpl::accessibilityFocusedUIElement()
3245 enableAccessibilityIfNecessary();
3246 return m_remoteAccessibilityChild.get();
3249 id WebViewImpl::accessibilityHitTest(CGPoint)
3251 return accessibilityFocusedUIElement();
3254 void WebViewImpl::enableAccessibilityIfNecessary()
3256 if (WebCore::AXObjectCache::accessibilityEnabled())
3259 // After enabling accessibility update the window frame on the web process so that the
3260 // correct accessibility position is transmitted (when AX is off, that position is not calculated).
3261 WebCore::AXObjectCache::enableAccessibility();
3262 updateWindowAndViewFrames();
3265 id WebViewImpl::accessibilityAttributeValue(NSString *attribute)
3267 enableAccessibilityIfNecessary();
3269 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
3272 if (m_remoteAccessibilityChild)
3273 child = m_remoteAccessibilityChild.get();
3277 return [NSArray arrayWithObject:child];
3279 if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
3280 return NSAccessibilityGroupRole;
3281 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
3282 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
3283 if ([attribute isEqualToString:NSAccessibilityParentAttribute])
3284 return NSAccessibilityUnignoredAncestor([m_view superview]);
3285 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
3288 return [m_view _web_superAccessibilityAttributeValue:attribute];
3291 void WebViewImpl::setPrimaryTrackingArea(NSTrackingArea *trackingArea)
3293 [m_view removeTrackingArea:m_primaryTrackingArea.get()];
3294 m_primaryTrackingArea = trackingArea;
3295 [m_view addTrackingArea:trackingArea];
3298 // Any non-zero value will do, but using something recognizable might help us debug some day.
3299 #define TRACKING_RECT_TAG 0xBADFACE
3301 NSTrackingRectTag WebViewImpl::addTrackingRect(CGRect, id owner, void* userData, bool assumeInside)
3303 ASSERT(m_trackingRectOwner == nil);
3304 m_trackingRectOwner = owner;
3305 m_trackingRectUserData = userData;
3306 return TRACKING_RECT_TAG;
3309 NSTrackingRectTag WebViewImpl::addTrackingRectWithTrackingNum(CGRect, id owner, void* userData, bool assumeInside, int tag)
3311 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
3312 ASSERT(m_trackingRectOwner == nil);
3313 m_trackingRectOwner = owner;
3314 m_trackingRectUserData = userData;
3315 return TRACKING_RECT_TAG;
3318 void WebViewImpl::addTrackingRectsWithTrackingNums(CGRect*, id owner, void** userDataList, bool assumeInside, NSTrackingRectTag *trackingNums, int count)
3321 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
3322 ASSERT(m_trackingRectOwner == nil);
3323 m_trackingRectOwner = owner;
3324 m_trackingRectUserData = userDataList[0];
3325 trackingNums[0] = TRACKING_RECT_TAG;
3328 void WebViewImpl::removeTrackingRect(NSTrackingRectTag tag)
3333 if (tag == TRACKING_RECT_TAG) {
3334 m_trackingRectOwner = nil;
3338 if (tag == m_lastToolTipTag) {
3339 [m_view _web_superRemoveTrackingRect:tag];
3340 m_lastToolTipTag = 0;
3344 // If any other tracking rect is being removed, we don't know how it was created
3345 // and it's possible there's a leak involved (see 3500217)
3346 ASSERT_NOT_REACHED();
3349 void WebViewImpl::removeTrackingRects(NSTrackingRectTag *tags, int count)
3351 for (int i = 0; i < count; ++i) {
3355 ASSERT(tag == TRACKING_RECT_TAG);
3356 m_trackingRectOwner = nil;
3360 void WebViewImpl::sendToolTipMouseExited()
3362 // Nothing matters except window, trackingNumber, and userData.
3363 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSEventTypeMouseExited
3364 location:NSMakePoint(0, 0)
3367 windowNumber:m_view.window.windowNumber
3370 trackingNumber:TRACKING_RECT_TAG
3371 userData:m_trackingRectUserData];
3372 [m_trackingRectOwner mouseExited:fakeEvent];
3375 void WebViewImpl::sendToolTipMouseEntered()
3377 // Nothing matters except window, trackingNumber, and userData.
3378 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSEventTypeMouseEntered
3379 location:NSMakePoint(0, 0)
3382 windowNumber:m_view.window.windowNumber
3385 trackingNumber:TRACKING_RECT_TAG
3386 userData:m_trackingRectUserData];
3387 [m_trackingRectOwner mouseEntered:fakeEvent];
3390 NSString *WebViewImpl::stringForToolTip(NSToolTipTag tag)
3392 return nsStringFromWebCoreString(m_page->toolTip());
3395 void WebViewImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip)
3397 if (!oldToolTip.isNull())
3398 sendToolTipMouseExited();
3400 if (!newToolTip.isEmpty()) {
3401 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
3402 [m_view removeAllToolTips];
3403 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
3404 m_lastToolTipTag = [m_view addToolTipRect:wideOpenRect owner:m_view userData:NULL];
3405 sendToolTipMouseEntered();
3409 void WebViewImpl::setAcceleratedCompositingRootLayer(CALayer *rootLayer)
3411 [rootLayer web_disableAllActions];
3413 m_rootLayer = rootLayer;
3416 if (m_thumbnailView) {
3417 updateThumbnailViewLayer();
3422 [CATransaction begin];
3423 [CATransaction setDisableActions:YES];
3426 if (!m_layerHostingView) {
3427 // Create an NSView that will host our layer tree.
3428 m_layerHostingView = adoptNS([[WKFlippedView alloc] initWithFrame:m_view.bounds]);
3429 [m_layerHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
3431 [m_view addSubview:m_layerHostingView.get() positioned:NSWindowBelow relativeTo:nil];
3433 // Create a root layer that will back the NSView.
3434 RetainPtr<CALayer> layer = adoptNS([[CALayer alloc] init]);
3435 [layer setDelegate:[WebActionDisablingCALayerDelegate shared]];
3437 [layer setName:@"Hosting root layer"];
3440 [m_layerHostingView setLayer:layer.get()];
3441 [m_layerHostingView setWantsLayer:YES];
3444 [m_layerHostingView layer].sublayers = [NSArray arrayWithObject:rootLayer];
3445 } else if (m_layerHostingView) {
3446 [m_layerHostingView removeFromSuperview];
3447 [m_layerHostingView setLayer:nil];
3448 [m_layerHostingView setWantsLayer:NO];
3450 m_layerHostingView = nullptr;
3453 [CATransaction commit];
3457 void WebViewImpl::setThumbnailView(_WKThumbnailView *thumbnailView)
3459 ASSERT(!m_thumbnailView || !thumbnailView);
3461 m_thumbnailView = thumbnailView;
3464 updateThumbnailViewLayer();
3466 setAcceleratedCompositingRootLayer(m_rootLayer.get());
3469 void WebViewImpl::reparentLayerTreeInThumbnailView()
3471 m_thumbnailView._thumbnailLayer = m_rootLayer.get();
3474 void WebViewImpl::updateThumbnailViewLayer()
3476 _WKThumbnailView *thumbnailView = m_thumbnailView;
3477 ASSERT(thumbnailView);
3479 if (thumbnailView._waitingForSnapshot && m_view.window)
3480 reparentLayerTreeInThumbnailView();
3483 void WebViewImpl::setInspectorAttachmentView(NSView *newView)
3485 NSView *oldView = m_inspectorAttachmentView.get();
3486 if (oldView == newView)
3489 m_inspectorAttachmentView = newView;
3490 m_page->inspector()->attachmentViewDidChange(oldView ? oldView : m_view, newView ? newView : m_view);
3493 NSView *WebViewImpl::inspectorAttachmentView()
3495 NSView *attachmentView = m_inspectorAttachmentView.get();
3496 return attachmentView ? attachmentView : m_view;
3499 _WKRemoteObjectRegistry *WebViewImpl::remoteObjectRegistry()
3501 if (!m_remoteObjectRegistry) {
3502 m_remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:m_page]);
3503 m_page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID(), [m_remoteObjectRegistry remoteObjectRegistry]);
3506 return m_remoteObjectRegistry.get();
3509 WKBrowsingContextController *WebViewImpl::browsingContextController()
3511 if (!m_browsingContextController)
3512 m_browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(m_page.ptr())]);
3514 return m_browsingContextController.get();
3516 #endif // WK_API_ENABLED
3518 #if ENABLE(DRAG_SUPPORT)
3519 void WebViewImpl::draggedImage(NSImage *image, CGPoint endPoint, NSDragOperation operation)
3521 #pragma clang diagnostic push
3522 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3523 NSPoint windowImageLoc = [m_view.window convertScreenToBase:NSPointFromCGPoint(endPoint)];
3524 #pragma clang diagnostic pop
3525 NSPoint windowMouseLoc = windowImageLoc;