2 * Copyright (C) 2015-2018 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 "APIAttachment.h"
32 #import "APILegacyContextHistoryClient.h"
33 #import "APINavigation.h"
34 #import "AttributedString.h"
35 #import "ColorSpaceData.h"
36 #import "FullscreenClient.h"
37 #import "GenericCallback.h"
39 #import "NativeWebGestureEvent.h"
40 #import "NativeWebKeyboardEvent.h"
41 #import "NativeWebMouseEvent.h"
42 #import "NativeWebWheelEvent.h"
43 #import "PageClient.h"
44 #import "PageClientImplMac.h"
45 #import "PasteboardTypes.h"
46 #import "PlaybackSessionManagerProxy.h"
47 #import "RemoteLayerTreeDrawingAreaProxy.h"
48 #import "RemoteObjectRegistry.h"
49 #import "RemoteObjectRegistryMessages.h"
50 #import "StringUtilities.h"
51 #import "TextChecker.h"
52 #import "TextCheckerState.h"
53 #import "TiledCoreAnimationDrawingAreaProxy.h"
54 #import "UIGamepadProvider.h"
55 #import "UndoOrRedo.h"
56 #import "ViewGestureController.h"
57 #import "WKBrowsingContextControllerInternal.h"
58 #import "WKErrorInternal.h"
59 #import "WKFullScreenWindowController.h"
60 #import "WKImmediateActionController.h"
61 #import "WKPrintingView.h"
62 #import "WKTextInputWindowController.h"
63 #import "WKViewLayoutStrategy.h"
64 #import "WKWebViewPrivate.h"
65 #import "WebBackForwardList.h"
66 #import "WebEditCommandProxy.h"
67 #import "WebEventFactory.h"
68 #import "WebInspectorProxy.h"
69 #import "WebPageProxy.h"
70 #import "WebProcessPool.h"
71 #import "WebProcessProxy.h"
72 #import "_WKRemoteObjectRegistryInternal.h"
73 #import "_WKThumbnailViewInternal.h"
74 #import <HIToolbox/CarbonEventsCore.h>
75 #import <WebCore/AXObjectCache.h>
76 #import <WebCore/ActivityState.h>
77 #import <WebCore/ColorMac.h>
78 #import <WebCore/DictionaryLookup.h>
79 #import <WebCore/DragData.h>
80 #import <WebCore/DragItem.h>
81 #import <WebCore/Editor.h>
82 #import <WebCore/FileSystem.h>
83 #import <WebCore/KeypressCommand.h>
84 #import <WebCore/LegacyNSPasteboardTypes.h>
85 #import <WebCore/LoaderNSURLExtras.h>
86 #import <WebCore/LocalizedStrings.h>
87 #import <WebCore/PlatformEventFactoryMac.h>
88 #import <WebCore/PromisedAttachmentInfo.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/WebPlaybackControlsManager.h>
96 #import <pal/spi/cg/CoreGraphicsSPI.h>
97 #import <pal/spi/cocoa/AVKitSPI.h>
98 #import <pal/spi/cocoa/NSTouchBarSPI.h>
99 #import <pal/spi/mac/DataDetectorsSPI.h>
100 #import <pal/spi/mac/LookupSPI.h>
101 #import <pal/spi/mac/NSAccessibilitySPI.h>
102 #import <pal/spi/mac/NSApplicationSPI.h>
103 #import <pal/spi/mac/NSImmediateActionGestureRecognizerSPI.h>
104 #import <pal/spi/mac/NSScrollerImpSPI.h>
105 #import <pal/spi/mac/NSSpellCheckerSPI.h>
106 #import <pal/spi/mac/NSTextFinderSPI.h>
107 #import <pal/spi/mac/NSWindowSPI.h>
109 #import <wtf/NeverDestroyed.h>
110 #import <wtf/ProcessPrivilege.h>
111 #import <wtf/SetForScope.h>
112 #import <wtf/SoftLinking.h>
113 #import <wtf/cf/TypeCastsCF.h>
114 #import <wtf/text/StringConcatenate.h>
116 #if HAVE(TOUCH_BAR) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
117 SOFT_LINK_FRAMEWORK(AVKit)
118 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
119 SOFT_LINK_CLASS(AVKit, AVTouchBarPlaybackControlsProvider)
120 SOFT_LINK_CLASS(AVKit, AVTouchBarScrubber)
122 SOFT_LINK_CLASS(AVKit, AVFunctionBarPlaybackControlsProvider)
123 SOFT_LINK_CLASS(AVKit, AVFunctionBarScrubber)
124 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
126 static NSString * const WKMediaExitFullScreenItem = @"WKMediaExitFullScreenItem";
127 #endif // HAVE(TOUCH_BAR) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
129 SOFT_LINK_CONSTANT_MAY_FAIL(Lookup, LUNotificationPopoverWillClose, NSString *)
131 WTF_DECLARE_CF_TYPE_TRAIT(CGImage);
133 @interface NSApplication ()
135 - (void)speakString:(NSString *)string;
136 - (void)stopSpeaking:(id)sender;
139 // FIXME: Move to an SPI header.
140 @interface NSTextInputContext (WKNSTextInputContextDetails)
141 - (void)handleEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler;
142 - (void)handleEventByInputMethod:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler;
143 - (BOOL)handleEventByKeyboardLayout:(NSEvent *)event;
146 #if HAVE(TOUCH_BAR) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
147 // FIXME: Remove this once -setCanShowMediaSelectionButton: is declared in an SDK used by Apple's buildbot.
148 @interface AVTouchBarScrubber ()
149 - (void)setCanShowMediaSelectionButton:(BOOL)canShowMediaSelectionButton;
153 @interface WKAccessibilitySettingsObserver : NSObject {
154 WebKit::WebViewImpl *_impl;
157 - (instancetype)initWithImpl:(WebKit::WebViewImpl&)impl;
160 @implementation WKAccessibilitySettingsObserver
162 - (instancetype)initWithImpl:(WebKit::WebViewImpl&)impl
170 NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
171 [workspaceNotificationCenter addObserver:self selector:@selector(_settingsDidChange:) name:NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification object:nil];
178 NSNotificationCenter *workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
179 [workspaceNotificationCenter removeObserver:self name:NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification object:nil];
184 - (void)_settingsDidChange:(NSNotification *)notification
186 _impl->accessibilitySettingsDidChange();
191 @interface WKWindowVisibilityObserver : NSObject {
193 WebKit::WebViewImpl *_impl;
195 BOOL _didRegisterForLookupPopoverCloseNotifications;
198 - (instancetype)initWithView:(NSView *)view impl:(WebKit::WebViewImpl&)impl;
199 - (void)startObserving:(NSWindow *)window;
200 - (void)stopObserving:(NSWindow *)window;
201 - (void)startObservingFontPanel;
202 - (void)startObservingLookupDismissalIfNeeded;
205 @implementation WKWindowVisibilityObserver
207 - (instancetype)initWithView:(NSView *)view impl:(WebKit::WebViewImpl&)impl
216 NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
217 [workspaceNotificationCenter addObserver:self selector:@selector(_activeSpaceDidChange:) name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
224 if (_didRegisterForLookupPopoverCloseNotifications && canLoadLUNotificationPopoverWillClose())
225 [[NSNotificationCenter defaultCenter] removeObserver:self name:getLUNotificationPopoverWillClose() object:nil];
227 NSNotificationCenter *workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
228 [workspaceNotificationCenter removeObserver:self name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
233 static void* keyValueObservingContext = &keyValueObservingContext;
235 - (void)startObserving:(NSWindow *)window
240 NSNotificationCenter *defaultNotificationCenter = [NSNotificationCenter defaultCenter];
242 // An NSView derived object such as WKView cannot observe these notifications, because NSView itself observes them.
243 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidOrderOffScreen:) name:@"NSWindowDidOrderOffScreenNotification" object:window];
244 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidOrderOnScreen:) name:@"_NSWindowDidBecomeVisible" object:window];
246 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
247 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
248 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
249 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
250 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidMove:) name:NSWindowDidMoveNotification object:window];
251 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidResize:) name:NSWindowDidResizeNotification object:window];
252 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeBackingProperties:) name:NSWindowDidChangeBackingPropertiesNotification object:window];
253 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification object:window];
254 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeLayerHosting:) name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
255 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeOcclusionState:) name:NSWindowDidChangeOcclusionStateNotification object:window];
257 [window addObserver:self forKeyPath:@"contentLayoutRect" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
258 [window addObserver:self forKeyPath:@"titlebarAppearsTransparent" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
261 - (void)stopObserving:(NSWindow *)window
266 NSNotificationCenter *defaultNotificationCenter = [NSNotificationCenter defaultCenter];
268 [defaultNotificationCenter removeObserver:self name:@"NSWindowDidOrderOffScreenNotification" object:window];
269 [defaultNotificationCenter removeObserver:self name:@"_NSWindowDidBecomeVisible" object:window];
271 [defaultNotificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
272 [defaultNotificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
273 [defaultNotificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
274 [defaultNotificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
275 [defaultNotificationCenter removeObserver:self name:NSWindowDidMoveNotification object:window];
276 [defaultNotificationCenter removeObserver:self name:NSWindowDidResizeNotification object:window];
277 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window];
278 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeScreenNotification object:window];
279 [defaultNotificationCenter removeObserver:self name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
280 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeOcclusionStateNotification object:window];
282 if (_impl->isEditable())
283 [[NSFontPanel sharedFontPanel] removeObserver:self forKeyPath:@"visible" context:keyValueObservingContext];
284 [window removeObserver:self forKeyPath:@"contentLayoutRect" context:keyValueObservingContext];
285 [window removeObserver:self forKeyPath:@"titlebarAppearsTransparent" context:keyValueObservingContext];
288 - (void)startObservingFontPanel
290 [[NSFontPanel sharedFontPanel] addObserver:self forKeyPath:@"visible" options:0 context:keyValueObservingContext];
293 - (void)startObservingLookupDismissalIfNeeded
295 if (_didRegisterForLookupPopoverCloseNotifications)
298 _didRegisterForLookupPopoverCloseNotifications = YES;
300 if (canLoadLUNotificationPopoverWillClose())
301 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_dictionaryLookupPopoverWillClose:) name:getLUNotificationPopoverWillClose() object:nil];
304 - (void)_windowDidOrderOnScreen:(NSNotification *)notification
306 _impl->windowDidOrderOnScreen();
309 - (void)_windowDidOrderOffScreen:(NSNotification *)notification
311 _impl->windowDidOrderOffScreen();
314 - (void)_windowDidBecomeKey:(NSNotification *)notification
316 _impl->windowDidBecomeKey([notification object]);
319 - (void)_windowDidResignKey:(NSNotification *)notification
321 _impl->windowDidResignKey([notification object]);
324 - (void)_windowDidMiniaturize:(NSNotification *)notification
326 _impl->windowDidMiniaturize();
329 - (void)_windowDidDeminiaturize:(NSNotification *)notification
331 _impl->windowDidDeminiaturize();
334 - (void)_windowDidMove:(NSNotification *)notification
336 _impl->windowDidMove();
339 - (void)_windowDidResize:(NSNotification *)notification
341 _impl->windowDidResize();
344 - (void)_windowDidChangeBackingProperties:(NSNotification *)notification
346 CGFloat oldBackingScaleFactor = [[notification.userInfo objectForKey:NSBackingPropertyOldScaleFactorKey] doubleValue];
347 _impl->windowDidChangeBackingProperties(oldBackingScaleFactor);
350 - (void)_windowDidChangeScreen:(NSNotification *)notification
352 _impl->windowDidChangeScreen();
355 - (void)_windowDidChangeLayerHosting:(NSNotification *)notification
357 _impl->windowDidChangeLayerHosting();
360 - (void)_windowDidChangeOcclusionState:(NSNotification *)notification
362 _impl->windowDidChangeOcclusionState();
365 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
367 if (context != keyValueObservingContext) {
368 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
372 if ([keyPath isEqualToString:@"visible"] && [NSFontPanel sharedFontPanelExists] && object == [NSFontPanel sharedFontPanel]) {
373 _impl->updateFontPanelIfNeeded();
376 if ([keyPath isEqualToString:@"contentLayoutRect"] || [keyPath isEqualToString:@"titlebarAppearsTransparent"])
377 _impl->updateContentInsetsIfAutomatic();
380 - (void)_dictionaryLookupPopoverWillClose:(NSNotification *)notification
382 _impl->clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::None);
385 - (void)_activeSpaceDidChange:(NSNotification *)notification
387 _impl->activeSpaceDidChange();
392 @interface WKEditCommandObjC : NSObject {
393 RefPtr<WebKit::WebEditCommandProxy> m_command;
395 - (id)initWithWebEditCommandProxy:(RefPtr<WebKit::WebEditCommandProxy>)command;
396 - (WebKit::WebEditCommandProxy*)command;
399 @interface WKEditorUndoTargetObjC : NSObject
400 - (void)undoEditing:(id)sender;
401 - (void)redoEditing:(id)sender;
404 @implementation WKEditCommandObjC
406 - (id)initWithWebEditCommandProxy:(RefPtr<WebKit::WebEditCommandProxy>)command
416 - (WebKit::WebEditCommandProxy*)command
418 return m_command.get();
423 @implementation WKEditorUndoTargetObjC
425 - (void)undoEditing:(id)sender
427 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
428 [sender command]->unapply();
431 - (void)redoEditing:(id)sender
433 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
434 [sender command]->reapply();
439 @interface WKFlippedView : NSView
442 @implementation WKFlippedView
451 @interface WKResponderChainSink : NSResponder {
452 NSResponder *_lastResponderInChain;
453 bool _didReceiveUnhandledCommand;
456 - (id)initWithResponderChain:(NSResponder *)chain;
458 - (bool)didReceiveUnhandledCommand;
461 @implementation WKResponderChainSink
463 - (id)initWithResponderChain:(NSResponder *)chain
468 _lastResponderInChain = chain;
469 while (NSResponder *next = [_lastResponderInChain nextResponder])
470 _lastResponderInChain = next;
471 [_lastResponderInChain setNextResponder:self];
477 // This assumes that the responder chain was either unmodified since
478 // -initWithResponderChain: was called, or was modified in such a way
479 // that _lastResponderInChain is still in the chain, and self was not
480 // moved earlier in the chain than _lastResponderInChain.
481 NSResponder *responderBeforeSelf = _lastResponderInChain;
482 NSResponder *next = [responderBeforeSelf nextResponder];
483 for (; next && next != self; next = [next nextResponder])
484 responderBeforeSelf = next;
486 // Nothing to be done if we are no longer in the responder chain.
490 [responderBeforeSelf setNextResponder:[self nextResponder]];
491 _lastResponderInChain = nil;
494 - (bool)didReceiveUnhandledCommand
496 return _didReceiveUnhandledCommand;
499 - (void)noResponderFor:(SEL)selector
501 _didReceiveUnhandledCommand = true;
504 - (void)doCommandBySelector:(SEL)selector
506 _didReceiveUnhandledCommand = true;
509 - (BOOL)tryToPerform:(SEL)action with:(id)object
511 _didReceiveUnhandledCommand = true;
517 using namespace WebKit;
521 @interface WKTextListTouchBarViewController : NSViewController {
523 WebViewImpl* _webViewImpl;
524 ListType _currentListType;
527 @property (nonatomic) ListType currentListType;
529 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl;
533 @implementation WKTextListTouchBarViewController
535 @synthesize currentListType=_currentListType;
537 static const CGFloat listControlSegmentWidth = 67;
538 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER) && ENABLE(FULLSCREEN_API)
539 static const CGFloat exitFullScreenButtonWidth = 64;
542 static const NSUInteger noListSegment = 0;
543 static const NSUInteger unorderedListSegment = 1;
544 static const NSUInteger orderedListSegment = 2;
546 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl
548 if (!(self = [super init]))
551 _webViewImpl = webViewImpl;
553 NSSegmentedControl *insertListControl = [NSSegmentedControl segmentedControlWithLabels:@[ WebCore::insertListTypeNone(), WebCore::insertListTypeBulleted(), WebCore::insertListTypeNumbered() ] trackingMode:NSSegmentSwitchTrackingSelectOne target:self action:@selector(_selectList:)];
554 [insertListControl setWidth:listControlSegmentWidth forSegment:noListSegment];
555 [insertListControl setWidth:listControlSegmentWidth forSegment:unorderedListSegment];
556 [insertListControl setWidth:listControlSegmentWidth forSegment:orderedListSegment];
557 insertListControl.font = [NSFont systemFontOfSize:15];
559 #pragma clang diagnostic push
560 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
561 id segmentElement = NSAccessibilityUnignoredDescendant(insertListControl);
562 NSArray *segments = [segmentElement accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
563 ASSERT(segments.count == 3);
564 [segments[noListSegment] accessibilitySetOverrideValue:WebCore::insertListTypeNone() forAttribute:NSAccessibilityDescriptionAttribute];
565 [segments[unorderedListSegment] accessibilitySetOverrideValue:WebCore::insertListTypeBulletedAccessibilityTitle() forAttribute:NSAccessibilityDescriptionAttribute];
566 [segments[orderedListSegment] accessibilitySetOverrideValue:WebCore::insertListTypeNumberedAccessibilityTitle() forAttribute:NSAccessibilityDescriptionAttribute];
567 #pragma clang diagnostic pop
569 self.view = insertListControl;
574 - (void)didDestroyView
576 _webViewImpl = nullptr;
579 - (void)_selectList:(id)sender
584 NSSegmentedControl *insertListControl = (NSSegmentedControl *)self.view;
585 switch (insertListControl.selectedSegment) {
587 // There is no "remove list" edit command, but InsertOrderedList and InsertUnorderedList both
588 // behave as toggles, so we can invoke the appropriate edit command depending on our _currentListType
589 // to remove an existing list. We don't have to do anything if _currentListType is NoList.
590 if (_currentListType == OrderedList)
591 _webViewImpl->page().executeEditCommand(@"InsertOrderedList", @"");
592 else if (_currentListType == UnorderedList)
593 _webViewImpl->page().executeEditCommand(@"InsertUnorderedList", @"");
595 case unorderedListSegment:
596 _webViewImpl->page().executeEditCommand(@"InsertUnorderedList", @"");
598 case orderedListSegment:
599 _webViewImpl->page().executeEditCommand(@"InsertOrderedList", @"");
603 _webViewImpl->dismissTextTouchBarPopoverItemWithIdentifier(NSTouchBarItemIdentifierTextList);
606 - (void)setCurrentListType:(ListType)listType
608 NSSegmentedControl *insertListControl = (NSSegmentedControl *)self.view;
611 [insertListControl setSelected:YES forSegment:noListSegment];
614 [insertListControl setSelected:YES forSegment:orderedListSegment];
617 [insertListControl setSelected:YES forSegment:unorderedListSegment];
621 _currentListType = listType;
626 @interface WKTextTouchBarItemController : NSTextTouchBarItemController <NSCandidateListTouchBarItemDelegate, NSTouchBarDelegate> {
630 BOOL _textIsUnderlined;
631 NSTextAlignment _currentTextAlignment;
632 RetainPtr<NSColor> _textColor;
633 RetainPtr<WKTextListTouchBarViewController> _textListTouchBarViewController;
636 WebViewImpl* _webViewImpl;
639 @property (nonatomic) BOOL textIsBold;
640 @property (nonatomic) BOOL textIsItalic;
641 @property (nonatomic) BOOL textIsUnderlined;
642 @property (nonatomic) NSTextAlignment currentTextAlignment;
643 @property (nonatomic, retain, readwrite) NSColor *textColor;
645 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl;
648 @implementation WKTextTouchBarItemController
650 @synthesize textIsBold=_textIsBold;
651 @synthesize textIsItalic=_textIsItalic;
652 @synthesize textIsUnderlined=_textIsUnderlined;
653 @synthesize currentTextAlignment=_currentTextAlignment;
655 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl
657 if (!(self = [super init]))
660 _webViewImpl = webViewImpl;
665 - (void)didDestroyView
667 [[NSNotificationCenter defaultCenter] removeObserver:self];
668 _webViewImpl = nullptr;
669 [_textListTouchBarViewController didDestroyView];
672 #pragma mark NSTouchBarDelegate
674 - (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSString *)identifier
676 return [self itemForIdentifier:identifier];
679 - (NSTouchBarItem *)itemForIdentifier:(NSString *)identifier
681 NSTouchBarItem *item = [super itemForIdentifier:identifier];
682 BOOL isTextFormatItem = [identifier isEqualToString:NSTouchBarItemIdentifierTextFormat];
684 if (isTextFormatItem || [identifier isEqualToString:NSTouchBarItemIdentifierTextStyle])
685 self.textStyle.action = @selector(_wkChangeTextStyle:);
687 if (isTextFormatItem || [identifier isEqualToString:NSTouchBarItemIdentifierTextAlignment])
688 self.textAlignments.action = @selector(_wkChangeTextAlignment:);
690 NSColorPickerTouchBarItem *colorPickerItem = nil;
691 if ([identifier isEqualToString:NSTouchBarItemIdentifierTextColorPicker] && [item isKindOfClass:[NSColorPickerTouchBarItem class]])
692 colorPickerItem = (NSColorPickerTouchBarItem *)item;
693 if (isTextFormatItem)
694 colorPickerItem = self.colorPickerItem;
695 if (colorPickerItem) {
696 colorPickerItem.target = self;
697 colorPickerItem.action = @selector(_wkChangeColor:);
698 colorPickerItem.showsAlpha = NO;
699 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
700 colorPickerItem.allowedColorSpaces = @[ [NSColorSpace sRGBColorSpace] ];
707 #pragma mark NSCandidateListTouchBarItemDelegate
709 - (void)candidateListTouchBarItem:(NSCandidateListTouchBarItem *)anItem endSelectingCandidateAtIndex:(NSInteger)index
711 if (index == NSNotFound)
717 NSArray *candidates = anItem.candidates;
718 if ((NSUInteger)index >= candidates.count)
721 NSTextCheckingResult *candidate = candidates[index];
722 ASSERT([candidate isKindOfClass:[NSTextCheckingResult class]]);
724 _webViewImpl->handleAcceptedCandidate(candidate);
727 - (void)candidateListTouchBarItem:(NSCandidateListTouchBarItem *)anItem changedCandidateListVisibility:(BOOL)isVisible
733 _webViewImpl->requestCandidatesForSelectionIfNeeded();
735 _webViewImpl->updateTouchBar();
738 #pragma mark NSNotificationCenter observers
740 - (void)touchBarDidExitCustomization:(NSNotification *)notification
745 _webViewImpl->setIsCustomizingTouchBar(false);
746 _webViewImpl->updateTouchBar();
749 - (void)touchBarWillEnterCustomization:(NSNotification *)notification
754 _webViewImpl->setIsCustomizingTouchBar(true);
757 - (void)didChangeAutomaticTextCompletion:(NSNotification *)notification
762 _webViewImpl->updateTouchBarAndRefreshTextBarIdentifiers();
766 #pragma mark NSTextTouchBarItemController
768 - (WKTextListTouchBarViewController *)textListTouchBarViewController
770 return (WKTextListTouchBarViewController *)self.textListViewController;
773 - (void)setTextIsBold:(BOOL)bold
776 if ([self.textStyle isSelectedForSegment:0] != _textIsBold)
777 [self.textStyle setSelected:_textIsBold forSegment:0];
780 - (void)setTextIsItalic:(BOOL)italic
782 _textIsItalic = italic;
783 if ([self.textStyle isSelectedForSegment:1] != _textIsItalic)
784 [self.textStyle setSelected:_textIsItalic forSegment:1];
787 - (void)setTextIsUnderlined:(BOOL)underlined
789 _textIsUnderlined = underlined;
790 if ([self.textStyle isSelectedForSegment:2] != _textIsUnderlined)
791 [self.textStyle setSelected:_textIsUnderlined forSegment:2];
794 - (void)_wkChangeTextStyle:(id)sender
799 if ([self.textStyle isSelectedForSegment:0] != _textIsBold) {
800 _textIsBold = !_textIsBold;
801 _webViewImpl->page().executeEditCommand(@"ToggleBold", @"");
804 if ([self.textStyle isSelectedForSegment:1] != _textIsItalic) {
805 _textIsItalic = !_textIsItalic;
806 _webViewImpl->page().executeEditCommand("ToggleItalic", @"");
809 if ([self.textStyle isSelectedForSegment:2] != _textIsUnderlined) {
810 _textIsUnderlined = !_textIsUnderlined;
811 _webViewImpl->page().executeEditCommand("ToggleUnderline", @"");
815 - (void)setCurrentTextAlignment:(NSTextAlignment)alignment
817 _currentTextAlignment = alignment;
818 [self.textAlignments selectSegmentWithTag:_currentTextAlignment];
821 - (void)_wkChangeTextAlignment:(id)sender
826 NSTextAlignment alignment = (NSTextAlignment)[self.textAlignments.cell tagForSegment:self.textAlignments.selectedSegment];
828 case NSTextAlignmentLeft:
829 _currentTextAlignment = NSTextAlignmentLeft;
830 _webViewImpl->page().executeEditCommand("AlignLeft", @"");
832 case NSTextAlignmentRight:
833 _currentTextAlignment = NSTextAlignmentRight;
834 _webViewImpl->page().executeEditCommand("AlignRight", @"");
836 case NSTextAlignmentCenter:
837 _currentTextAlignment = NSTextAlignmentCenter;
838 _webViewImpl->page().executeEditCommand("AlignCenter", @"");
840 case NSTextAlignmentJustified:
841 _currentTextAlignment = NSTextAlignmentJustified;
842 _webViewImpl->page().executeEditCommand("AlignJustified", @"");
848 _webViewImpl->dismissTextTouchBarPopoverItemWithIdentifier(NSTouchBarItemIdentifierTextAlignment);
851 - (NSColor *)textColor
853 return _textColor.get();
856 - (void)setTextColor:(NSColor *)color
859 self.colorPickerItem.color = _textColor.get();
862 - (void)_wkChangeColor:(id)sender
867 _textColor = self.colorPickerItem.color;
868 _webViewImpl->page().executeEditCommand("ForeColor", WebCore::colorFromNSColor(_textColor.get()).serialized());
871 - (NSViewController *)textListViewController
873 if (!_textListTouchBarViewController)
874 _textListTouchBarViewController = adoptNS([[WKTextListTouchBarViewController alloc] initWithWebViewImpl:_webViewImpl]);
875 return _textListTouchBarViewController.get();
880 @interface WKPromisedAttachmentContext : NSObject {
882 RetainPtr<NSURL> _blobURL;
883 RetainPtr<NSString> _fileName;
884 RetainPtr<NSString> _attachmentIdentifier;
887 - (instancetype)initWithIdentifier:(NSString *)identifier blobURL:(NSURL *)url fileName:(NSString *)fileName;
889 @property (nonatomic, readonly) NSURL *blobURL;
890 @property (nonatomic, readonly) NSString *fileName;
891 @property (nonatomic, readonly) NSString *attachmentIdentifier;
895 @implementation WKPromisedAttachmentContext
897 - (instancetype)initWithIdentifier:(NSString *)identifier blobURL:(NSURL *)blobURL fileName:(NSString *)fileName
899 if (!(self = [super init]))
903 _fileName = fileName;
904 _attachmentIdentifier = identifier;
910 return _blobURL.get();
913 - (NSString *)fileName
915 return _fileName.get();
918 - (NSString *)attachmentIdentifier
920 return _attachmentIdentifier.get();
927 NSTouchBar *WebViewImpl::makeTouchBar()
929 if (!m_canCreateTouchBars) {
930 m_canCreateTouchBars = true;
933 return m_currentTouchBar.get();
936 void WebViewImpl::updateTouchBar()
938 if (!m_canCreateTouchBars)
941 NSTouchBar *touchBar = nil;
942 bool userActionRequirementsHaveBeenMet = m_requiresUserActionForEditingControlsManager ? m_page->hasHadSelectionChangesFromUserInteraction() : true;
943 if (m_page->editorState().isContentEditable && !m_page->needsHiddenContentEditableQuirk()) {
944 updateTextTouchBar();
945 if (userActionRequirementsHaveBeenMet)
946 touchBar = textTouchBar();
948 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
949 else if (m_page->hasActiveVideoForControlsManager()) {
950 updateMediaTouchBar();
951 // If useMediaPlaybackControlsView() is true, then we are relying on the API client to display a popover version
952 // of the media timeline in their own function bar. If it is false, then we will display the media timeline in our
954 if (!useMediaPlaybackControlsView())
955 touchBar = [m_mediaTouchBarProvider respondsToSelector:@selector(touchBar)] ? [(id)m_mediaTouchBarProvider.get() touchBar] : [(id)m_mediaTouchBarProvider.get() touchBar];
956 } else if ([m_mediaTouchBarProvider playbackControlsController]) {
957 if (m_clientWantsMediaPlaybackControlsView) {
958 if ([m_view respondsToSelector:@selector(_web_didRemoveMediaControlsManager)] && m_view.getAutoreleased() == [m_view window].firstResponder)
959 [m_view _web_didRemoveMediaControlsManager];
961 [m_mediaTouchBarProvider setPlaybackControlsController:nil];
962 [m_mediaPlaybackControlsView setPlaybackControlsController:nil];
966 if (touchBar == m_currentTouchBar)
969 // If m_editableElementIsFocused is true, then we may have a non-editable selection right now just because
970 // the user is clicking or tabbing between editable fields.
971 if (m_editableElementIsFocused && touchBar != textTouchBar())
974 m_currentTouchBar = touchBar;
975 [m_view willChangeValueForKey:@"touchBar"];
976 [m_view setTouchBar:m_currentTouchBar.get()];
977 [m_view didChangeValueForKey:@"touchBar"];
980 NSCandidateListTouchBarItem *WebViewImpl::candidateListTouchBarItem() const
982 if (m_page->editorState().isInPasswordField)
983 return m_passwordTextCandidateListTouchBarItem.get();
984 return isRichlyEditable() ? m_richTextCandidateListTouchBarItem.get() : m_plainTextCandidateListTouchBarItem.get();
987 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
988 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
989 AVTouchBarScrubber *WebViewImpl::mediaPlaybackControlsView() const
991 AVFunctionBarScrubber *WebViewImpl::mediaPlaybackControlsView() const
994 if (m_page->hasActiveVideoForControlsManager())
995 return m_mediaPlaybackControlsView.get();
1000 bool WebViewImpl::useMediaPlaybackControlsView() const
1002 #if ENABLE(FULLSCREEN_API)
1003 if (hasFullScreenWindowController())
1004 return ![m_fullScreenWindowController isFullScreen];
1006 return m_clientWantsMediaPlaybackControlsView;
1009 void WebViewImpl::dismissTextTouchBarPopoverItemWithIdentifier(NSString *identifier)
1011 NSTouchBarItem *foundItem = nil;
1012 for (NSTouchBarItem *item in textTouchBar().items) {
1013 if ([item.identifier isEqualToString:identifier]) {
1018 if ([item.identifier isEqualToString:NSTouchBarItemIdentifierTextFormat]) {
1019 for (NSTouchBarItem *childItem in ((NSGroupTouchBarItem *)item).groupTouchBar.items) {
1020 if ([childItem.identifier isEqualToString:identifier]) {
1021 foundItem = childItem;
1029 if ([foundItem isKindOfClass:[NSPopoverTouchBarItem class]])
1030 [(NSPopoverTouchBarItem *)foundItem dismissPopover:nil];
1033 static NSArray<NSString *> *textTouchBarCustomizationAllowedIdentifiers()
1035 return @[ NSTouchBarItemIdentifierCharacterPicker, NSTouchBarItemIdentifierTextColorPicker, NSTouchBarItemIdentifierTextStyle, NSTouchBarItemIdentifierTextAlignment, NSTouchBarItemIdentifierTextList, NSTouchBarItemIdentifierFlexibleSpace ];
1038 static NSArray<NSString *> *plainTextTouchBarDefaultItemIdentifiers()
1040 return @[ NSTouchBarItemIdentifierCharacterPicker, NSTouchBarItemIdentifierCandidateList ];
1043 static NSArray<NSString *> *richTextTouchBarDefaultItemIdentifiers()
1045 return @[ NSTouchBarItemIdentifierCharacterPicker, NSTouchBarItemIdentifierTextFormat, NSTouchBarItemIdentifierCandidateList ];
1048 static NSArray<NSString *> *passwordTextTouchBarDefaultItemIdentifiers()
1050 return @[ NSTouchBarItemIdentifierCandidateList ];
1053 void WebViewImpl::updateTouchBarAndRefreshTextBarIdentifiers()
1055 if (m_richTextTouchBar)
1056 setUpTextTouchBar(m_richTextTouchBar.get());
1058 if (m_plainTextTouchBar)
1059 setUpTextTouchBar(m_plainTextTouchBar.get());
1061 if (m_passwordTextTouchBar)
1062 setUpTextTouchBar(m_passwordTextTouchBar.get());
1067 void WebViewImpl::setUpTextTouchBar(NSTouchBar *touchBar)
1069 NSSet<NSTouchBarItem *> *templateItems = nil;
1070 NSArray<NSTouchBarItemIdentifier> *defaultItemIdentifiers = nil;
1071 NSArray<NSTouchBarItemIdentifier> *customizationAllowedItemIdentifiers = nil;
1073 if (touchBar == m_passwordTextTouchBar.get()) {
1074 templateItems = [NSMutableSet setWithObject:m_passwordTextCandidateListTouchBarItem.get()];
1075 defaultItemIdentifiers = passwordTextTouchBarDefaultItemIdentifiers();
1076 } else if (touchBar == m_richTextTouchBar.get()) {
1077 templateItems = [NSMutableSet setWithObject:m_richTextCandidateListTouchBarItem.get()];
1078 defaultItemIdentifiers = richTextTouchBarDefaultItemIdentifiers();
1079 customizationAllowedItemIdentifiers = textTouchBarCustomizationAllowedIdentifiers();
1080 } else if (touchBar == m_plainTextTouchBar.get()) {
1081 templateItems = [NSMutableSet setWithObject:m_plainTextCandidateListTouchBarItem.get()];
1082 defaultItemIdentifiers = plainTextTouchBarDefaultItemIdentifiers();
1083 customizationAllowedItemIdentifiers = textTouchBarCustomizationAllowedIdentifiers();
1086 [touchBar setDelegate:m_textTouchBarItemController.get()];
1087 [touchBar setTemplateItems:templateItems];
1088 [touchBar setDefaultItemIdentifiers:defaultItemIdentifiers];
1089 [touchBar setCustomizationAllowedItemIdentifiers:customizationAllowedItemIdentifiers];
1091 if (NSGroupTouchBarItem *textFormatItem = (NSGroupTouchBarItem *)[touchBar itemForIdentifier:NSTouchBarItemIdentifierTextFormat])
1092 textFormatItem.groupTouchBar.customizationIdentifier = @"WKTextFormatTouchBar";
1095 bool WebViewImpl::isRichlyEditable() const
1097 return m_page->editorState().isContentRichlyEditable && !m_page->needsPlainTextQuirk();
1100 NSTouchBar *WebViewImpl::textTouchBar() const
1102 if (m_page->editorState().isInPasswordField)
1103 return m_passwordTextTouchBar.get();
1104 return isRichlyEditable() ? m_richTextTouchBar.get() : m_plainTextTouchBar.get();
1107 static NSTextAlignment nsTextAlignmentFromTextAlignment(TextAlignment textAlignment)
1109 NSTextAlignment nsTextAlignment;
1110 switch (textAlignment) {
1112 nsTextAlignment = NSTextAlignmentNatural;
1115 nsTextAlignment = NSTextAlignmentLeft;
1117 case RightAlignment:
1118 nsTextAlignment = NSTextAlignmentRight;
1120 case CenterAlignment:
1121 nsTextAlignment = NSTextAlignmentCenter;
1123 case JustifiedAlignment:
1124 nsTextAlignment = NSTextAlignmentJustified;
1127 ASSERT_NOT_REACHED();
1130 return nsTextAlignment;
1133 void WebViewImpl::updateTextTouchBar()
1135 if (!m_page->editorState().isContentEditable)
1138 if (m_isUpdatingTextTouchBar)
1141 SetForScope<bool> isUpdatingTextFunctionBar(m_isUpdatingTextTouchBar, true);
1143 if (!m_textTouchBarItemController)
1144 m_textTouchBarItemController = adoptNS([[WKTextTouchBarItemController alloc] initWithWebViewImpl:this]);
1146 if (!m_startedListeningToCustomizationEvents) {
1147 [[NSNotificationCenter defaultCenter] addObserver:m_textTouchBarItemController.get() selector:@selector(touchBarDidExitCustomization:) name:NSTouchBarDidExitCustomization object:nil];
1148 [[NSNotificationCenter defaultCenter] addObserver:m_textTouchBarItemController.get() selector:@selector(touchBarWillEnterCustomization:) name:NSTouchBarWillEnterCustomization object:nil];
1149 [[NSNotificationCenter defaultCenter] addObserver:m_textTouchBarItemController.get() selector:@selector(didChangeAutomaticTextCompletion:) name:NSSpellCheckerDidChangeAutomaticTextCompletionNotification object:nil];
1151 m_startedListeningToCustomizationEvents = true;
1154 if (!m_richTextCandidateListTouchBarItem || !m_plainTextCandidateListTouchBarItem || !m_passwordTextCandidateListTouchBarItem) {
1155 m_richTextCandidateListTouchBarItem = adoptNS([[NSCandidateListTouchBarItem alloc] initWithIdentifier:NSTouchBarItemIdentifierCandidateList]);
1156 [m_richTextCandidateListTouchBarItem setDelegate:m_textTouchBarItemController.get()];
1157 m_plainTextCandidateListTouchBarItem = adoptNS([[NSCandidateListTouchBarItem alloc] initWithIdentifier:NSTouchBarItemIdentifierCandidateList]);
1158 [m_plainTextCandidateListTouchBarItem setDelegate:m_textTouchBarItemController.get()];
1159 m_passwordTextCandidateListTouchBarItem = adoptNS([[NSCandidateListTouchBarItem alloc] initWithIdentifier:NSTouchBarItemIdentifierCandidateList]);
1160 [m_passwordTextCandidateListTouchBarItem setDelegate:m_textTouchBarItemController.get()];
1161 requestCandidatesForSelectionIfNeeded();
1164 if (!m_richTextTouchBar) {
1165 m_richTextTouchBar = adoptNS([[NSTouchBar alloc] init]);
1166 setUpTextTouchBar(m_richTextTouchBar.get());
1167 [m_richTextTouchBar setCustomizationIdentifier:@"WKRichTextTouchBar"];
1170 if (!m_plainTextTouchBar) {
1171 m_plainTextTouchBar = adoptNS([[NSTouchBar alloc] init]);
1172 setUpTextTouchBar(m_plainTextTouchBar.get());
1173 [m_plainTextTouchBar setCustomizationIdentifier:@"WKPlainTextTouchBar"];
1176 if ([NSSpellChecker isAutomaticTextCompletionEnabled] && !m_isCustomizingTouchBar) {
1177 BOOL showCandidatesList = !m_page->editorState().selectionIsRange || m_isHandlingAcceptedCandidate;
1178 [candidateListTouchBarItem() updateWithInsertionPointVisibility:showCandidatesList];
1179 [m_view _didUpdateCandidateListVisibility:showCandidatesList];
1182 if (m_page->editorState().isInPasswordField) {
1183 if (!m_passwordTextTouchBar) {
1184 m_passwordTextTouchBar = adoptNS([[NSTouchBar alloc] init]);
1185 setUpTextTouchBar(m_passwordTextTouchBar.get());
1187 [m_passwordTextCandidateListTouchBarItem setCandidates:@[ ] forSelectedRange:NSMakeRange(0, 0) inString:nil];
1190 NSTouchBar *textTouchBar = this->textTouchBar();
1191 BOOL isShowingCombinedTextFormatItem = [textTouchBar.defaultItemIdentifiers containsObject:NSTouchBarItemIdentifierTextFormat];
1192 [textTouchBar setPrincipalItemIdentifier:isShowingCombinedTextFormatItem ? NSTouchBarItemIdentifierTextFormat : nil];
1194 // Set current typing attributes for rich text. This will ensure that the buttons reflect the state of
1195 // the text when changing selection throughout the document.
1196 if (isRichlyEditable()) {
1197 const EditorState& editorState = m_page->editorState();
1198 if (!editorState.isMissingPostLayoutData) {
1199 [m_textTouchBarItemController setTextIsBold:(bool)(m_page->editorState().postLayoutData().typingAttributes & AttributeBold)];
1200 [m_textTouchBarItemController setTextIsItalic:(bool)(m_page->editorState().postLayoutData().typingAttributes & AttributeItalics)];
1201 [m_textTouchBarItemController setTextIsUnderlined:(bool)(m_page->editorState().postLayoutData().typingAttributes & AttributeUnderline)];
1202 [m_textTouchBarItemController setTextColor:nsColor(editorState.postLayoutData().textColor)];
1203 [[m_textTouchBarItemController textListTouchBarViewController] setCurrentListType:(ListType)m_page->editorState().postLayoutData().enclosingListType];
1204 [m_textTouchBarItemController setCurrentTextAlignment:nsTextAlignmentFromTextAlignment((TextAlignment)editorState.postLayoutData().textAlignment)];
1206 BOOL isShowingCandidateListItem = [textTouchBar.defaultItemIdentifiers containsObject:NSTouchBarItemIdentifierCandidateList] && [NSSpellChecker isAutomaticTextReplacementEnabled];
1207 [m_textTouchBarItemController setUsesNarrowTextStyleItem:isShowingCombinedTextFormatItem && isShowingCandidateListItem];
1211 void WebViewImpl::updateMediaTouchBar()
1213 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER) && ENABLE(VIDEO_PRESENTATION_MODE)
1214 if (!m_mediaTouchBarProvider) {
1215 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
1216 m_mediaTouchBarProvider = adoptNS([allocAVTouchBarPlaybackControlsProviderInstance() init]);
1218 m_mediaTouchBarProvider = adoptNS([allocAVFunctionBarPlaybackControlsProviderInstance() init]);
1219 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
1222 if (!m_mediaPlaybackControlsView) {
1223 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
1224 m_mediaPlaybackControlsView = adoptNS([allocAVTouchBarScrubberInstance() init]);
1225 // FIXME: Remove this once setCanShowMediaSelectionButton: is declared in an SDK used by Apple's buildbot.
1226 if ([m_mediaPlaybackControlsView respondsToSelector:@selector(setCanShowMediaSelectionButton:)])
1227 [m_mediaPlaybackControlsView setCanShowMediaSelectionButton:YES];
1229 m_mediaPlaybackControlsView = adoptNS([allocAVFunctionBarScrubberInstance() init]);
1230 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
1233 if (!m_playbackControlsManager) {
1234 m_playbackControlsManager = adoptNS([[WebPlaybackControlsManager alloc] init]);
1235 [m_playbackControlsManager setAllowsPictureInPicturePlayback:m_page->preferences().allowsPictureInPictureMediaPlayback()];
1236 [m_playbackControlsManager setCanTogglePictureInPicture:YES];
1239 if (PlatformPlaybackSessionInterface* interface = m_page->playbackSessionManager()->controlsManagerInterface())
1240 [m_playbackControlsManager setPlaybackSessionInterfaceMac:interface];
1242 [m_mediaTouchBarProvider setPlaybackControlsController:m_playbackControlsManager.get()];
1243 [m_mediaPlaybackControlsView setPlaybackControlsController:m_playbackControlsManager.get()];
1245 if (!useMediaPlaybackControlsView()) {
1246 #if ENABLE(FULLSCREEN_API)
1247 // If we can't have a media popover function bar item, it might be because we are in full screen.
1248 // If so, customize the escape key.
1249 NSTouchBar *touchBar = [m_mediaTouchBarProvider respondsToSelector:@selector(touchBar)] ? [(id)m_mediaTouchBarProvider.get() touchBar] : [(id)m_mediaTouchBarProvider.get() touchBar];
1250 if (hasFullScreenWindowController() && [m_fullScreenWindowController isFullScreen]) {
1251 if (!m_exitFullScreenButton) {
1252 m_exitFullScreenButton = adoptNS([[NSCustomTouchBarItem alloc] initWithIdentifier:WKMediaExitFullScreenItem]);
1254 NSImage *image = [NSImage imageNamed:NSImageNameTouchBarExitFullScreenTemplate];
1255 [image setTemplate:YES];
1257 NSButton *exitFullScreenButton = [NSButton buttonWithTitle:image ? @"" : @"Exit" image:image target:m_fullScreenWindowController.get() action:@selector(requestExitFullScreen)];
1258 [exitFullScreenButton setAccessibilityTitle:WebCore::exitFullScreenButtonAccessibilityTitle()];
1260 [[exitFullScreenButton.widthAnchor constraintLessThanOrEqualToConstant:exitFullScreenButtonWidth] setActive:YES];
1261 [m_exitFullScreenButton setView:exitFullScreenButton];
1263 touchBar.escapeKeyReplacementItem = m_exitFullScreenButton.get();
1265 touchBar.escapeKeyReplacementItem = nil;
1267 // The rest of the work to update the media function bar only applies to the popover version, so return early.
1271 if (m_playbackControlsManager && m_view.getAutoreleased() == [m_view window].firstResponder && [m_view respondsToSelector:@selector(_web_didAddMediaControlsManager:)])
1272 [m_view _web_didAddMediaControlsManager:m_mediaPlaybackControlsView.get()];
1276 void WebViewImpl::forceRequestCandidatesForTesting()
1278 m_canCreateTouchBars = true;
1284 bool WebViewImpl::shouldRequestCandidates() const
1286 return !m_page->editorState().isInPasswordField && candidateListTouchBarItem().candidateListVisible;
1289 void WebViewImpl::setEditableElementIsFocused(bool editableElementIsFocused)
1291 m_editableElementIsFocused = editableElementIsFocused;
1294 // If the editable elements have blurred, then we might need to get rid of the editing function bar.
1295 if (!m_editableElementIsFocused)
1300 } // namespace WebKit
1301 #else // !HAVE(TOUCH_BAR)
1304 void WebViewImpl::forceRequestCandidatesForTesting()
1308 bool WebViewImpl::shouldRequestCandidates() const
1313 void WebViewImpl::setEditableElementIsFocused(bool editableElementIsFocused)
1315 m_editableElementIsFocused = editableElementIsFocused;
1318 } // namespace WebKit
1319 #endif // HAVE(TOUCH_BAR)
1323 static NSTrackingAreaOptions trackingAreaOptions()
1325 // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
1326 NSTrackingAreaOptions options = NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingInVisibleRect | NSTrackingCursorUpdate;
1327 if (_NSRecommendedScrollerStyle() == NSScrollerStyleLegacy)
1328 options |= NSTrackingActiveAlways;
1330 options |= NSTrackingActiveInKeyWindow;
1334 WebViewImpl::WebViewImpl(NSView <WebViewImplDelegate> *view, WKWebView *outerWebView, WebProcessPool& processPool, Ref<API::PageConfiguration>&& configuration)
1336 , m_pageClient(std::make_unique<PageClientImpl>(view, outerWebView))
1337 , m_page(processPool.createWebPage(*m_pageClient, WTFMove(configuration)))
1338 , m_needsViewFrameInWindowCoordinates(m_page->preferences().pluginsEnabled())
1339 , m_intrinsicContentSize(CGSizeMake(NSViewNoIntrinsicMetric, NSViewNoIntrinsicMetric))
1340 , m_layoutStrategy([WKViewLayoutStrategy layoutStrategyWithPage:m_page view:view viewImpl:*this mode:kWKLayoutModeViewSize])
1341 , m_undoTarget(adoptNS([[WKEditorUndoTargetObjC alloc] init]))
1342 , m_windowVisibilityObserver(adoptNS([[WKWindowVisibilityObserver alloc] initWithView:view impl:*this]))
1343 , m_accessibilitySettingsObserver(adoptNS([[WKAccessibilitySettingsObserver alloc] initWithImpl:*this]))
1344 , m_primaryTrackingArea(adoptNS([[NSTrackingArea alloc] initWithRect:view.frame options:trackingAreaOptions() owner:view userInfo:nil]))
1346 static_cast<PageClientImpl&>(*m_pageClient).setImpl(*this);
1348 ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
1349 [NSApp registerServicesMenuSendTypes:PasteboardTypes::forSelection() returnTypes:PasteboardTypes::forEditing()];
1351 [view addTrackingArea:m_primaryTrackingArea.get()];
1353 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1355 if (Class gestureClass = NSClassFromString(@"NSImmediateActionGestureRecognizer")) {
1356 m_immediateActionGestureRecognizer = adoptNS([(NSImmediateActionGestureRecognizer *)[gestureClass alloc] init]);
1357 m_immediateActionController = adoptNS([[WKImmediateActionController alloc] initWithPage:m_page view:view viewImpl:*this recognizer:m_immediateActionGestureRecognizer.get()]);
1358 [m_immediateActionGestureRecognizer setDelegate:m_immediateActionController.get()];
1359 [m_immediateActionGestureRecognizer setDelaysPrimaryMouseButtonEvents:NO];
1362 m_page->setAddsVisitedLinks(processPool.historyClient().addsVisitedLinks());
1364 m_page->initializeWebPage();
1366 registerDraggedTypes();
1368 view.wantsLayer = YES;
1370 // Explicitly set the layer contents placement so AppKit will make sure that our layer has masksToBounds set to YES.
1371 view.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
1373 #if ENABLE(FULLSCREEN_API) && WK_API_ENABLED
1374 m_page->setFullscreenClient(std::make_unique<WebKit::FullscreenClient>(view));
1377 WebProcessPool::statistics().wkViewCount++;
1380 WebViewImpl::~WebViewImpl()
1383 if (m_remoteObjectRegistry) {
1384 m_page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID());
1385 [m_remoteObjectRegistry _invalidate];
1386 m_remoteObjectRegistry = nil;
1390 ASSERT(!m_inSecureInputState);
1393 ASSERT(!m_thumbnailView);
1396 [m_layoutStrategy invalidate];
1398 [m_immediateActionController willDestroyView:m_view.getAutoreleased()];
1401 [m_textTouchBarItemController didDestroyView];
1402 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
1403 [m_mediaTouchBarProvider setPlaybackControlsController:nil];
1404 [m_mediaPlaybackControlsView setPlaybackControlsController:nil];
1410 WebProcessPool::statistics().wkViewCount--;
1414 NSWindow *WebViewImpl::window()
1416 return [m_view window];
1419 void WebViewImpl::processDidExit()
1421 dismissContentRelativeChildWindowsWithAnimation(true);
1423 notifyInputContextAboutDiscardedComposition();
1425 if (m_layerHostingView)
1426 setAcceleratedCompositingRootLayer(nil);
1428 updateRemoteAccessibilityRegistration(false);
1429 flushPendingMouseEventCallbacks();
1431 m_gestureController = nullptr;
1434 void WebViewImpl::pageClosed()
1436 updateRemoteAccessibilityRegistration(false);
1439 void WebViewImpl::didRelaunchProcess()
1441 accessibilityRegisterUIProcessTokens();
1444 void WebViewImpl::setDrawsBackground(bool drawsBackground)
1446 m_page->setDrawsBackground(drawsBackground);
1448 // Make sure updateLayer gets called on the web view.
1449 [m_view setNeedsDisplay:YES];
1452 bool WebViewImpl::drawsBackground() const
1454 return m_page->drawsBackground();
1457 void WebViewImpl::setBackgroundColor(NSColor *backgroundColor)
1459 m_backgroundColor = backgroundColor;
1461 // Make sure updateLayer gets called on the web view.
1462 [m_view setNeedsDisplay:YES];
1465 NSColor *WebViewImpl::backgroundColor() const
1467 if (!m_backgroundColor)
1468 return [NSColor whiteColor];
1469 return m_backgroundColor.get();
1472 bool WebViewImpl::isOpaque() const
1474 return m_page->drawsBackground();
1477 void WebViewImpl::setShouldSuppressFirstResponderChanges(bool shouldSuppress)
1479 m_pageClient->setShouldSuppressFirstResponderChanges(shouldSuppress);
1482 bool WebViewImpl::acceptsFirstResponder()
1487 bool WebViewImpl::becomeFirstResponder()
1489 ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
1490 // If we just became first responder again, there is no need to do anything,
1491 // since resignFirstResponder has correctly detected this situation.
1492 if (m_willBecomeFirstResponderAgain) {
1493 m_willBecomeFirstResponderAgain = false;
1497 NSSelectionDirection direction = [[m_view window] keyViewSelectionDirection];
1499 m_inBecomeFirstResponder = true;
1501 updateSecureInputState();
1502 m_page->activityStateDidChange(WebCore::ActivityState::IsFocused);
1503 // Restore the selection in the editable region if resigning first responder cleared selection.
1504 m_page->restoreSelectionInFocusedEditableElement();
1506 m_inBecomeFirstResponder = false;
1512 if (direction != NSDirectSelection) {
1513 NSEvent *event = [NSApp currentEvent];
1514 NSEvent *keyboardEvent = nil;
1515 if ([event type] == NSEventTypeKeyDown || [event type] == NSEventTypeKeyUp)
1516 keyboardEvent = event;
1517 m_page->setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, false, { }), [](WebKit::CallbackBase::Error) { });
1522 bool WebViewImpl::resignFirstResponder()
1525 // Predict the case where we are losing first responder status only to
1526 // gain it back again. We want resignFirstResponder to do nothing in that case.
1527 id nextResponder = [[m_view window] _newFirstResponderAfterResigning];
1529 // FIXME: This will probably need to change once WKWebView doesn't contain a WKView.
1530 if ([nextResponder isKindOfClass:[WKWebView class]] && [m_view superview] == nextResponder) {
1531 m_willBecomeFirstResponderAgain = true;
1536 m_willBecomeFirstResponderAgain = false;
1537 m_inResignFirstResponder = true;
1539 m_page->confirmCompositionAsync();
1541 notifyInputContextAboutDiscardedComposition();
1543 resetSecureInputState();
1545 if (!m_page->maintainsInactiveSelection())
1546 m_page->clearSelection();
1548 m_page->activityStateDidChange(WebCore::ActivityState::IsFocused);
1550 m_inResignFirstResponder = false;
1555 bool WebViewImpl::isFocused() const
1557 if (m_inBecomeFirstResponder)
1559 if (m_inResignFirstResponder)
1561 return [m_view window].firstResponder == m_view.getAutoreleased();
1564 void WebViewImpl::viewWillStartLiveResize()
1566 m_page->viewWillStartLiveResize();
1568 [m_layoutStrategy willStartLiveResize];
1571 void WebViewImpl::viewDidEndLiveResize()
1573 m_page->viewWillEndLiveResize();
1575 [m_layoutStrategy didEndLiveResize];
1578 void WebViewImpl::renewGState()
1580 if (m_textIndicatorWindow)
1581 dismissContentRelativeChildWindowsWithAnimation(false);
1583 // Update the view frame.
1584 if ([m_view window])
1585 updateWindowAndViewFrames();
1587 updateContentInsetsIfAutomatic();
1590 void WebViewImpl::setFrameSize(CGSize)
1592 [m_layoutStrategy didChangeFrameSize];
1595 void WebViewImpl::disableFrameSizeUpdates()
1597 [m_layoutStrategy disableFrameSizeUpdates];
1600 void WebViewImpl::enableFrameSizeUpdates()
1602 [m_layoutStrategy enableFrameSizeUpdates];
1605 bool WebViewImpl::frameSizeUpdatesDisabled() const
1607 return [m_layoutStrategy frameSizeUpdatesDisabled];
1610 void WebViewImpl::setFrameAndScrollBy(CGRect frame, CGSize scrollDelta)
1612 if (!CGSizeEqualToSize(scrollDelta, CGSizeZero))
1613 m_scrollOffsetAdjustment = scrollDelta;
1615 [m_view frame] = NSRectFromCGRect(frame);
1618 void WebViewImpl::updateWindowAndViewFrames()
1620 if (clipsToVisibleRect())
1621 updateViewExposedRect();
1623 if (m_didScheduleWindowAndViewFrameUpdate)
1626 m_didScheduleWindowAndViewFrameUpdate = true;
1628 auto weakThis = makeWeakPtr(*this);
1629 dispatch_async(dispatch_get_main_queue(), [weakThis] {
1633 weakThis->m_didScheduleWindowAndViewFrameUpdate = false;
1635 NSRect viewFrameInWindowCoordinates = NSZeroRect;
1636 NSPoint accessibilityPosition = NSZeroPoint;
1638 if (weakThis->m_needsViewFrameInWindowCoordinates)
1639 viewFrameInWindowCoordinates = [weakThis->m_view convertRect:[weakThis->m_view frame] toView:nil];
1641 #pragma clang diagnostic push
1642 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1643 if (WebCore::AXObjectCache::accessibilityEnabled())
1644 accessibilityPosition = [[weakThis->m_view accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
1645 #pragma clang diagnostic pop
1647 weakThis->m_page->windowAndViewFramesChanged(viewFrameInWindowCoordinates, accessibilityPosition);
1651 void WebViewImpl::setFixedLayoutSize(CGSize fixedLayoutSize)
1653 m_lastRequestedFixedLayoutSize = fixedLayoutSize;
1655 if (supportsArbitraryLayoutModes())
1656 m_page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
1659 CGSize WebViewImpl::fixedLayoutSize() const
1661 return m_page->fixedLayoutSize();
1664 std::unique_ptr<WebKit::DrawingAreaProxy> WebViewImpl::createDrawingAreaProxy()
1666 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"WebKit2UseRemoteLayerTreeDrawingArea"] boolValue])
1667 return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(m_page);
1669 return std::make_unique<TiledCoreAnimationDrawingAreaProxy>(m_page);
1672 bool WebViewImpl::isUsingUISideCompositing() const
1674 auto* drawingArea = m_page->drawingArea();
1675 return drawingArea && drawingArea->type() == DrawingAreaTypeRemoteLayerTree;
1678 void WebViewImpl::setDrawingAreaSize(CGSize size)
1680 if (!m_page->drawingArea())
1683 m_page->drawingArea()->setSize(WebCore::IntSize(size), WebCore::IntSize(m_scrollOffsetAdjustment));
1684 m_scrollOffsetAdjustment = CGSizeZero;
1687 void WebViewImpl::updateLayer()
1689 bool draws = drawsBackground();
1690 if (!draws || !m_backgroundColor)
1691 [m_view layer].backgroundColor = CGColorGetConstantColor(draws ? kCGColorWhite : kCGColorClear);
1693 [m_view layer].backgroundColor = [m_backgroundColor CGColor];
1696 void WebViewImpl::drawRect(CGRect rect)
1698 LOG(Printing, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
1699 m_page->endPrinting();
1702 bool WebViewImpl::canChangeFrameLayout(WebFrameProxy& frame)
1704 // PDF documents are already paginated, so we can't change them to add headers and footers.
1705 return !frame.isDisplayingPDFDocument();
1708 NSPrintOperation *WebViewImpl::printOperationWithPrintInfo(NSPrintInfo *printInfo, WebFrameProxy& frame)
1710 LOG(Printing, "Creating an NSPrintOperation for frame '%s'", frame.url().string().utf8().data());
1712 // FIXME: If the frame cannot be printed (e.g. if it contains an encrypted PDF that disallows
1713 // printing), this function should return nil.
1714 RetainPtr<WKPrintingView> printingView = adoptNS([[WKPrintingView alloc] initWithFrameProxy:frame view:m_view.getAutoreleased()]);
1715 // NSPrintOperation takes ownership of the view.
1716 NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get() printInfo:printInfo];
1717 [printOperation setCanSpawnSeparateThread:YES];
1718 [printOperation setJobTitle:frame.title()];
1719 printingView->_printOperation = printOperation;
1720 return printOperation;
1723 void WebViewImpl::setAutomaticallyAdjustsContentInsets(bool automaticallyAdjustsContentInsets)
1725 m_automaticallyAdjustsContentInsets = automaticallyAdjustsContentInsets;
1726 updateContentInsetsIfAutomatic();
1729 void WebViewImpl::updateContentInsetsIfAutomatic()
1731 if (!m_automaticallyAdjustsContentInsets)
1734 NSWindow *window = [m_view window];
1735 if ((window.styleMask & NSWindowStyleMaskFullSizeContentView) && !window.titlebarAppearsTransparent && ![m_view enclosingScrollView]) {
1736 NSRect contentLayoutRect = [m_view convertRect:window.contentLayoutRect fromView:nil];
1737 CGFloat newTopContentInset = NSMaxY(contentLayoutRect) - NSHeight(contentLayoutRect);
1738 if (m_page->topContentInset() != newTopContentInset)
1739 setTopContentInset(newTopContentInset);
1741 setTopContentInset(0);
1744 CGFloat WebViewImpl::topContentInset() const
1746 if (m_didScheduleSetTopContentInset)
1747 return m_pendingTopContentInset;
1748 return m_page->topContentInset();
1751 void WebViewImpl::setTopContentInset(CGFloat contentInset)
1753 m_pendingTopContentInset = contentInset;
1755 if (m_didScheduleSetTopContentInset)
1758 m_didScheduleSetTopContentInset = true;
1760 auto weakThis = makeWeakPtr(*this);
1761 dispatch_async(dispatch_get_main_queue(), [weakThis] {
1764 weakThis->dispatchSetTopContentInset();
1768 void WebViewImpl::dispatchSetTopContentInset()
1770 if (!m_didScheduleSetTopContentInset)
1773 m_didScheduleSetTopContentInset = false;
1774 m_page->setTopContentInset(m_pendingTopContentInset);
1777 void WebViewImpl::prepareContentInRect(CGRect rect)
1779 m_contentPreparationRect = rect;
1780 m_useContentPreparationRectForVisibleRect = true;
1782 updateViewExposedRect();
1785 void WebViewImpl::updateViewExposedRect()
1787 CGRect exposedRect = NSRectToCGRect([m_view visibleRect]);
1789 if (m_useContentPreparationRectForVisibleRect)
1790 exposedRect = CGRectUnion(m_contentPreparationRect, exposedRect);
1792 if (auto drawingArea = m_page->drawingArea())
1793 drawingArea->setViewExposedRect(m_clipsToVisibleRect ? std::optional<WebCore::FloatRect>(exposedRect) : std::nullopt);
1796 void WebViewImpl::setClipsToVisibleRect(bool clipsToVisibleRect)
1798 m_clipsToVisibleRect = clipsToVisibleRect;
1799 updateViewExposedRect();
1802 void WebViewImpl::setMinimumSizeForAutoLayout(CGSize minimumSizeForAutoLayout)
1804 bool expandsToFit = minimumSizeForAutoLayout.width > 0;
1806 m_page->setViewLayoutSize(WebCore::IntSize(minimumSizeForAutoLayout));
1807 m_page->setMainFrameIsScrollable(!expandsToFit);
1809 setClipsToVisibleRect(expandsToFit);
1812 CGSize WebViewImpl::minimumSizeForAutoLayout() const
1814 return m_page->viewLayoutSize();
1817 void WebViewImpl::setShouldExpandToViewHeightForAutoLayout(bool shouldExpandToViewHeightForAutoLayout)
1819 m_page->setAutoSizingShouldExpandToViewHeight(shouldExpandToViewHeightForAutoLayout);
1822 bool WebViewImpl::shouldExpandToViewHeightForAutoLayout() const
1824 return m_page->autoSizingShouldExpandToViewHeight();
1827 void WebViewImpl::setIntrinsicContentSize(CGSize intrinsicContentSize)
1829 // If the intrinsic content size is less than the minimum layout width, the content flowed to fit,
1830 // so we can report that that dimension is flexible. If not, we need to report our intrinsic width
1831 // so that autolayout will know to provide space for us.
1833 CGSize intrinsicContentSizeAcknowledgingFlexibleWidth = intrinsicContentSize;
1834 if (intrinsicContentSize.width < m_page->viewLayoutSize().width())
1835 intrinsicContentSizeAcknowledgingFlexibleWidth.width = NSViewNoIntrinsicMetric;
1837 m_intrinsicContentSize = intrinsicContentSizeAcknowledgingFlexibleWidth;
1838 [m_view invalidateIntrinsicContentSize];
1841 CGSize WebViewImpl::intrinsicContentSize() const
1843 return m_intrinsicContentSize;
1846 void WebViewImpl::setViewScale(CGFloat viewScale)
1848 m_lastRequestedViewScale = viewScale;
1850 if (!supportsArbitraryLayoutModes() && viewScale != 1)
1853 if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
1854 [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
1856 m_page->scaleView(viewScale);
1857 [m_layoutStrategy didChangeViewScale];
1860 CGFloat WebViewImpl::viewScale() const
1862 return m_page->viewScaleFactor();
1865 WKLayoutMode WebViewImpl::layoutMode() const
1867 return [m_layoutStrategy layoutMode];
1870 void WebViewImpl::setLayoutMode(WKLayoutMode layoutMode)
1872 m_lastRequestedLayoutMode = layoutMode;
1874 if (!supportsArbitraryLayoutModes() && layoutMode != kWKLayoutModeViewSize)
1877 if (layoutMode == [m_layoutStrategy layoutMode])
1880 [m_layoutStrategy willChangeLayoutStrategy];
1881 m_layoutStrategy = [WKViewLayoutStrategy layoutStrategyWithPage:m_page view:m_view.getAutoreleased() viewImpl:*this mode:layoutMode];
1884 bool WebViewImpl::supportsArbitraryLayoutModes() const
1886 if ([m_fullScreenWindowController isFullScreen])
1889 WebFrameProxy* frame = m_page->mainFrame();
1893 // If we have a plugin document in the main frame, avoid using custom WKLayoutModes
1894 // and fall back to the defaults, because there's a good chance that it won't work (e.g. with PDFPlugin).
1895 if (frame->containsPluginDocument())
1901 void WebViewImpl::updateSupportsArbitraryLayoutModes()
1903 if (!supportsArbitraryLayoutModes()) {
1904 WKLayoutMode oldRequestedLayoutMode = m_lastRequestedLayoutMode;
1905 CGFloat oldRequestedViewScale = m_lastRequestedViewScale;
1906 CGSize oldRequestedFixedLayoutSize = m_lastRequestedFixedLayoutSize;
1908 setLayoutMode(kWKLayoutModeViewSize);
1909 setFixedLayoutSize(CGSizeZero);
1911 // The 'last requested' parameters will have been overwritten by setting them above, but we don't
1912 // want this to count as a request (only changes from the client count), so reset them.
1913 m_lastRequestedLayoutMode = oldRequestedLayoutMode;
1914 m_lastRequestedViewScale = oldRequestedViewScale;
1915 m_lastRequestedFixedLayoutSize = oldRequestedFixedLayoutSize;
1916 } else if (m_lastRequestedLayoutMode != [m_layoutStrategy layoutMode]) {
1917 setViewScale(m_lastRequestedViewScale);
1918 setLayoutMode(m_lastRequestedLayoutMode);
1919 setFixedLayoutSize(m_lastRequestedFixedLayoutSize);
1923 void WebViewImpl::setOverrideDeviceScaleFactor(CGFloat deviceScaleFactor)
1925 m_overrideDeviceScaleFactor = deviceScaleFactor;
1926 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1929 float WebViewImpl::intrinsicDeviceScaleFactor() const
1931 if (m_overrideDeviceScaleFactor)
1932 return m_overrideDeviceScaleFactor;
1933 if (m_targetWindowForMovePreparation)
1934 return m_targetWindowForMovePreparation.backingScaleFactor;
1935 if (NSWindow *window = [m_view window])
1936 return window.backingScaleFactor;
1937 return [NSScreen mainScreen].backingScaleFactor;
1940 void WebViewImpl::windowDidOrderOffScreen()
1942 LOG(ActivityState, "WebViewImpl %p (page %llu) windowDidOrderOffScreen", this, m_page->pageID());
1943 m_page->activityStateDidChange({ WebCore::ActivityState::IsVisible, WebCore::ActivityState::WindowIsActive });
1946 void WebViewImpl::windowDidOrderOnScreen()
1948 LOG(ActivityState, "WebViewImpl %p (page %llu) windowDidOrderOnScreen", this, m_page->pageID());
1949 m_page->activityStateDidChange({ WebCore::ActivityState::IsVisible, WebCore::ActivityState::WindowIsActive });
1952 void WebViewImpl::windowDidBecomeKey(NSWindow *keyWindow)
1954 if (keyWindow == [m_view window] || keyWindow == [m_view window].attachedSheet) {
1956 UIGamepadProvider::singleton().viewBecameActive(m_page.get());
1958 updateSecureInputState();
1959 m_page->activityStateDidChange(WebCore::ActivityState::WindowIsActive);
1963 void WebViewImpl::windowDidResignKey(NSWindow *formerKeyWindow)
1965 if (formerKeyWindow == [m_view window] || formerKeyWindow == [m_view window].attachedSheet) {
1967 UIGamepadProvider::singleton().viewBecameInactive(m_page.get());
1969 updateSecureInputState();
1970 m_page->activityStateDidChange(WebCore::ActivityState::WindowIsActive);
1974 void WebViewImpl::windowDidMiniaturize()
1976 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1979 void WebViewImpl::windowDidDeminiaturize()
1981 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1984 void WebViewImpl::windowDidMove()
1986 updateWindowAndViewFrames();
1989 void WebViewImpl::windowDidResize()
1991 updateWindowAndViewFrames();
1994 void WebViewImpl::windowDidChangeBackingProperties(CGFloat oldBackingScaleFactor)
1996 CGFloat newBackingScaleFactor = intrinsicDeviceScaleFactor();
1997 if (oldBackingScaleFactor == newBackingScaleFactor)
2000 m_page->setIntrinsicDeviceScaleFactor(newBackingScaleFactor);
2003 void WebViewImpl::windowDidChangeScreen()
2005 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : [m_view window];
2006 m_page->windowScreenDidChange([[[[window screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue]);
2009 void WebViewImpl::windowDidChangeLayerHosting()
2011 m_page->layerHostingModeDidChange();
2014 void WebViewImpl::windowDidChangeOcclusionState()
2016 LOG(ActivityState, "WebViewImpl %p (page %llu) windowDidChangeOcclusionState", this, m_page->pageID());
2017 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
2020 bool WebViewImpl::mightBeginDragWhileInactive()
2022 if ([m_view window].isKeyWindow)
2025 if (m_page->editorState().selectionIsNone || !m_page->editorState().selectionIsRange)
2031 bool WebViewImpl::mightBeginScrollWhileInactive()
2033 // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
2034 if (_NSRecommendedScrollerStyle() == NSScrollerStyleLegacy)
2040 void WebViewImpl::accessibilitySettingsDidChange()
2042 m_page->accessibilitySettingsDidChange();
2045 bool WebViewImpl::acceptsFirstMouse(NSEvent *event)
2047 if (!mightBeginDragWhileInactive() && !mightBeginScrollWhileInactive())
2050 // There's a chance that responding to this event will run a nested event loop, and
2051 // fetching a new event might release the old one. Retaining and then autoreleasing
2052 // the current event prevents that from causing a problem inside WebKit or AppKit code.
2053 [[event retain] autorelease];
2055 if (![m_view hitTest:event.locationInWindow])
2058 setLastMouseDownEvent(event);
2059 bool result = m_page->acceptsFirstMouse(event.eventNumber, WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view.getAutoreleased()));
2060 setLastMouseDownEvent(nil);
2064 bool WebViewImpl::shouldDelayWindowOrderingForEvent(NSEvent *event)
2066 if (!mightBeginDragWhileInactive())
2069 // There's a chance that responding to this event will run a nested event loop, and
2070 // fetching a new event might release the old one. Retaining and then autoreleasing
2071 // the current event prevents that from causing a problem inside WebKit or AppKit code.
2072 [[event retain] autorelease];
2074 if (![m_view hitTest:event.locationInWindow])
2077 setLastMouseDownEvent(event);
2078 bool result = m_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view.getAutoreleased()));
2079 setLastMouseDownEvent(nil);
2083 bool WebViewImpl::windowResizeMouseLocationIsInVisibleScrollerThumb(CGPoint point)
2085 NSPoint localPoint = [m_view convertPoint:NSPointFromCGPoint(point) fromView:nil];
2086 NSRect visibleThumbRect = NSRect(m_page->visibleScrollerThumbRect());
2087 return NSMouseInRect(localPoint, visibleThumbRect, [m_view isFlipped]);
2090 void WebViewImpl::viewWillMoveToWindow(NSWindow *window)
2092 // If we're in the middle of preparing to move to a window, we should only be moved to that window.
2093 ASSERT(!m_targetWindowForMovePreparation || (m_targetWindowForMovePreparation == window));
2095 NSWindow *currentWindow = [m_view window];
2096 if (window == currentWindow)
2099 clearAllEditCommands();
2101 NSWindow *stopObservingWindow = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : [m_view window];
2102 [m_windowVisibilityObserver stopObserving:stopObservingWindow];
2103 [m_windowVisibilityObserver startObserving:window];
2106 void WebViewImpl::viewDidMoveToWindow()
2108 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : [m_view window];
2110 LOG(ActivityState, "WebViewImpl %p viewDidMoveToWindow %p", this, window);
2113 windowDidChangeScreen();
2115 OptionSet<WebCore::ActivityState::Flag> activityStateChanges { WebCore::ActivityState::WindowIsActive, WebCore::ActivityState::IsVisible };
2116 if (m_shouldDeferViewInWindowChanges)
2117 m_viewInWindowChangeWasDeferred = true;
2119 activityStateChanges |= WebCore::ActivityState::IsInWindow;
2120 m_page->activityStateDidChange(activityStateChanges);
2122 updateWindowAndViewFrames();
2124 // FIXME(135509) This call becomes unnecessary once 135509 is fixed; remove.
2125 m_page->layerHostingModeDidChange();
2127 if (!m_flagsChangedEventMonitor) {
2128 auto weakThis = makeWeakPtr(*this);
2129 m_flagsChangedEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskFlagsChanged handler:[weakThis] (NSEvent *flagsChangedEvent) {
2131 weakThis->postFakeMouseMovedEventForFlagsChangedEvent(flagsChangedEvent);
2132 return flagsChangedEvent;
2136 accessibilityRegisterUIProcessTokens();
2138 if (m_immediateActionGestureRecognizer && ![[m_view gestureRecognizers] containsObject:m_immediateActionGestureRecognizer.get()] && !m_ignoresNonWheelEvents && m_allowsLinkPreview)
2139 [m_view addGestureRecognizer:m_immediateActionGestureRecognizer.get()];
2141 OptionSet<WebCore::ActivityState::Flag> activityStateChanges { WebCore::ActivityState::WindowIsActive, WebCore::ActivityState::IsVisible };
2142 if (m_shouldDeferViewInWindowChanges)
2143 m_viewInWindowChangeWasDeferred = true;
2145 activityStateChanges |= WebCore::ActivityState::IsInWindow;
2146 m_page->activityStateDidChange(activityStateChanges);
2148 [NSEvent removeMonitor:m_flagsChangedEventMonitor];
2149 m_flagsChangedEventMonitor = nil;
2151 dismissContentRelativeChildWindowsWithAnimation(false);
2153 if (m_immediateActionGestureRecognizer) {
2154 // Work around <rdar://problem/22646404> by explicitly cancelling the animation.
2155 cancelImmediateActionAnimation();
2156 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
2160 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
2163 void WebViewImpl::viewDidChangeBackingProperties()
2165 NSColorSpace *colorSpace = [m_view window].colorSpace;
2166 if ([colorSpace isEqualTo:m_colorSpace.get()])
2169 m_colorSpace = nullptr;
2170 if (DrawingAreaProxy *drawingArea = m_page->drawingArea())
2171 drawingArea->colorSpaceDidChange();
2174 void WebViewImpl::viewDidHide()
2176 LOG(ActivityState, "WebViewImpl %p (page %llu) viewDidHide", this, m_page->pageID());
2177 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
2180 void WebViewImpl::viewDidUnhide()
2182 LOG(ActivityState, "WebViewImpl %p (page %llu) viewDidUnhide", this, m_page->pageID());
2183 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
2186 void WebViewImpl::activeSpaceDidChange()
2188 LOG(ActivityState, "WebViewImpl %p (page %llu) activeSpaceDidChange", this, m_page->pageID());
2189 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
2192 NSView *WebViewImpl::hitTest(CGPoint point)
2194 NSView *hitView = [m_view _web_superHitTest:NSPointFromCGPoint(point)];
2195 if (hitView && hitView == m_layerHostingView)
2196 hitView = m_view.getAutoreleased();
2201 void WebViewImpl::postFakeMouseMovedEventForFlagsChangedEvent(NSEvent *flagsChangedEvent)
2203 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSEventTypeMouseMoved location:flagsChangedEvent.window.mouseLocationOutsideOfEventStream
2204 modifierFlags:flagsChangedEvent.modifierFlags timestamp:flagsChangedEvent.timestamp windowNumber:flagsChangedEvent.windowNumber
2205 context:nullptr eventNumber:0 clickCount:0 pressure:0];
2206 NativeWebMouseEvent webEvent(fakeEvent, m_lastPressureEvent.get(), m_view.getAutoreleased());
2207 m_page->handleMouseEvent(webEvent);
2210 ColorSpaceData WebViewImpl::colorSpace()
2212 if (!m_colorSpace) {
2213 if (m_targetWindowForMovePreparation)
2214 m_colorSpace = m_targetWindowForMovePreparation.colorSpace;
2215 else if (NSWindow *window = [m_view window])
2216 m_colorSpace = window.colorSpace;
2218 m_colorSpace = [NSScreen mainScreen].colorSpace;
2221 ColorSpaceData colorSpaceData;
2222 colorSpaceData.cgColorSpace = [m_colorSpace CGColorSpace];
2224 return colorSpaceData;
2227 void WebViewImpl::setUnderlayColor(NSColor *underlayColor)
2229 m_page->setUnderlayColor(WebCore::colorFromNSColor(underlayColor));
2232 NSColor *WebViewImpl::underlayColor() const
2234 WebCore::Color webColor = m_page->underlayColor();
2235 if (!webColor.isValid())
2238 return WebCore::nsColor(webColor);
2241 NSColor *WebViewImpl::pageExtendedBackgroundColor() const
2243 WebCore::Color color = m_page->pageExtendedBackgroundColor();
2244 if (!color.isValid())
2247 return WebCore::nsColor(color);
2250 void WebViewImpl::setOverlayScrollbarStyle(std::optional<WebCore::ScrollbarOverlayStyle> scrollbarStyle)
2252 m_page->setOverlayScrollbarStyle(scrollbarStyle);
2255 std::optional<WebCore::ScrollbarOverlayStyle> WebViewImpl::overlayScrollbarStyle() const
2257 return m_page->overlayScrollbarStyle();
2260 void WebViewImpl::beginDeferringViewInWindowChanges()
2262 if (m_shouldDeferViewInWindowChanges) {
2263 NSLog(@"beginDeferringViewInWindowChanges was called while already deferring view-in-window changes!");
2267 m_shouldDeferViewInWindowChanges = true;
2270 void WebViewImpl::endDeferringViewInWindowChanges()
2272 if (!m_shouldDeferViewInWindowChanges) {
2273 NSLog(@"endDeferringViewInWindowChanges was called without beginDeferringViewInWindowChanges!");
2277 m_shouldDeferViewInWindowChanges = false;
2279 if (m_viewInWindowChangeWasDeferred) {
2280 dispatchSetTopContentInset();
2281 m_page->activityStateDidChange(WebCore::ActivityState::IsInWindow);
2282 m_viewInWindowChangeWasDeferred = false;
2286 void WebViewImpl::endDeferringViewInWindowChangesSync()
2288 if (!m_shouldDeferViewInWindowChanges) {
2289 NSLog(@"endDeferringViewInWindowChangesSync was called without beginDeferringViewInWindowChanges!");
2293 m_shouldDeferViewInWindowChanges = false;
2295 if (m_viewInWindowChangeWasDeferred) {
2296 dispatchSetTopContentInset();
2297 m_page->activityStateDidChange(WebCore::ActivityState::IsInWindow);
2298 m_viewInWindowChangeWasDeferred = false;
2302 void WebViewImpl::prepareForMoveToWindow(NSWindow *targetWindow, WTF::Function<void()>&& completionHandler)
2304 m_shouldDeferViewInWindowChanges = true;
2305 viewWillMoveToWindow(targetWindow);
2306 m_targetWindowForMovePreparation = targetWindow;
2307 viewDidMoveToWindow();
2309 m_shouldDeferViewInWindowChanges = false;
2311 auto weakThis = makeWeakPtr(*this);
2312 m_page->installActivityStateChangeCompletionHandler([weakThis, completionHandler = WTFMove(completionHandler)]() {
2313 completionHandler();
2318 ASSERT([weakThis->m_view window] == weakThis->m_targetWindowForMovePreparation);
2319 weakThis->m_targetWindowForMovePreparation = nil;
2322 dispatchSetTopContentInset();
2323 m_page->activityStateDidChange(WebCore::ActivityState::IsInWindow, false, WebPageProxy::ActivityStateChangeDispatchMode::Immediate);
2324 m_viewInWindowChangeWasDeferred = false;
2327 void WebViewImpl::updateSecureInputState()
2329 if (![[m_view window] isKeyWindow] || !isFocused()) {
2330 if (m_inSecureInputState) {
2331 DisableSecureEventInput();
2332 m_inSecureInputState = false;
2336 // WKView has a single input context for all editable areas (except for plug-ins).
2337 NSTextInputContext *context = [m_view _web_superInputContext];
2338 bool isInPasswordField = m_page->editorState().isInPasswordField;
2340 if (isInPasswordField) {
2341 if (!m_inSecureInputState)
2342 EnableSecureEventInput();
2343 static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1];
2344 LOG(TextInput, "-> setAllowedInputSourceLocales:romanInputSources");
2345 [context setAllowedInputSourceLocales:romanInputSources];
2347 if (m_inSecureInputState)
2348 DisableSecureEventInput();
2349 LOG(TextInput, "-> setAllowedInputSourceLocales:nil");
2350 [context setAllowedInputSourceLocales:nil];
2352 m_inSecureInputState = isInPasswordField;
2355 void WebViewImpl::resetSecureInputState()
2357 if (m_inSecureInputState) {
2358 DisableSecureEventInput();
2359 m_inSecureInputState = false;
2363 void WebViewImpl::notifyInputContextAboutDiscardedComposition()
2365 // <rdar://problem/9359055>: -discardMarkedText can only be called for active contexts.
2366 // FIXME: We fail to ever notify the input context if something (e.g. a navigation) happens while the window is not key.
2367 // This is not a problem when the window is key, because we discard marked text on resigning first responder.
2368 if (![[m_view window] isKeyWindow] || m_view.getAutoreleased() != [[m_view window] firstResponder])
2371 LOG(TextInput, "-> discardMarkedText");
2373 [[m_view _web_superInputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to.
2376 void WebViewImpl::setPluginComplexTextInputState(PluginComplexTextInputState pluginComplexTextInputState)
2378 m_pluginComplexTextInputState = pluginComplexTextInputState;
2380 if (m_pluginComplexTextInputState != PluginComplexTextInputDisabled)
2383 // Send back an empty string to the plug-in. This will disable text input.
2384 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, String());
2387 void WebViewImpl::setPluginComplexTextInputStateAndIdentifier(PluginComplexTextInputState pluginComplexTextInputState, uint64_t pluginComplexTextInputIdentifier)
2389 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier) {
2390 // We're asked to update the state for a plug-in that doesn't have focus.
2394 setPluginComplexTextInputState(pluginComplexTextInputState);
2397 void WebViewImpl::disableComplexTextInputIfNecessary()
2399 if (!m_pluginComplexTextInputIdentifier)
2402 if (m_pluginComplexTextInputState != PluginComplexTextInputEnabled)
2405 // Check if the text input window has been dismissed.
2406 if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
2407 setPluginComplexTextInputState(PluginComplexTextInputDisabled);
2410 bool WebViewImpl::handlePluginComplexTextInputKeyDown(NSEvent *event)
2412 ASSERT(m_pluginComplexTextInputIdentifier);
2413 ASSERT(m_pluginComplexTextInputState != PluginComplexTextInputDisabled);
2415 BOOL usingLegacyCocoaTextInput = m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
2417 NSString *string = nil;
2418 BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string];
2421 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, string);
2423 if (!usingLegacyCocoaTextInput)
2424 m_pluginComplexTextInputState = PluginComplexTextInputDisabled;
2427 return didHandleEvent;
2430 bool WebViewImpl::tryHandlePluginComplexTextInputKeyDown(NSEvent *event)
2432 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
2435 // Check if the text input window has been dismissed and let the plug-in process know.
2436 // This is only valid with the updated Cocoa text input spec.
2437 disableComplexTextInputIfNecessary();
2439 // Try feeding the keyboard event directly to the plug-in.
2440 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
2441 return handlePluginComplexTextInputKeyDown(event);
2446 void WebViewImpl::pluginFocusOrWindowFocusChanged(bool pluginHasFocusAndWindowHasFocus, uint64_t pluginComplexTextInputIdentifier)
2448 ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
2449 BOOL inputSourceChanged = m_pluginComplexTextInputIdentifier;
2451 if (pluginHasFocusAndWindowHasFocus) {
2452 // Check if we're already allowing text input for this plug-in.
2453 if (pluginComplexTextInputIdentifier == m_pluginComplexTextInputIdentifier)
2456 m_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier;
2459 // Check if we got a request to unfocus a plug-in that isn't focused.
2460 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier)
2463 m_pluginComplexTextInputIdentifier = 0;
2466 if (inputSourceChanged) {
2467 // The input source changed; discard any entered text.
2468 [[WKTextInputWindowController sharedTextInputWindowController] unmarkText];
2471 // This will force the current input context to be updated to its correct value.
2472 [NSApp updateWindows];
2475 bool WebViewImpl::tryPostProcessPluginComplexTextInputKeyDown(NSEvent *event)
2477 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
2480 // In the legacy text input model, the event has already been sent to the input method.
2481 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
2484 return handlePluginComplexTextInputKeyDown(event);
2487 void WebViewImpl::handleAcceptedAlternativeText(const String& acceptedAlternative)
2489 m_page->handleAlternativeTextUIResult(acceptedAlternative);
2493 NSInteger WebViewImpl::spellCheckerDocumentTag()
2495 if (!m_spellCheckerDocumentTag)
2496 m_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
2497 return m_spellCheckerDocumentTag.value();
2500 void WebViewImpl::pressureChangeWithEvent(NSEvent *event)
2502 #if defined(__LP64__)
2503 if (event == m_lastPressureEvent)
2506 if (m_ignoresNonWheelEvents)
2509 if (event.phase != NSEventPhaseChanged && event.phase != NSEventPhaseBegan && event.phase != NSEventPhaseEnded)
2512 NativeWebMouseEvent webEvent(event, m_lastPressureEvent.get(), m_view.getAutoreleased());
2513 m_page->handleMouseEvent(webEvent);
2515 m_lastPressureEvent = event;
2519 #if ENABLE(FULLSCREEN_API)
2520 bool WebViewImpl::hasFullScreenWindowController() const
2522 return !!m_fullScreenWindowController;
2525 WKFullScreenWindowController *WebViewImpl::fullScreenWindowController()
2527 if (!m_fullScreenWindowController)
2528 m_fullScreenWindowController = adoptNS([[WKFullScreenWindowController alloc] initWithWindow:fullScreenWindow() webView:m_view.getAutoreleased() page:m_page]);
2530 return m_fullScreenWindowController.get();
2533 void WebViewImpl::closeFullScreenWindowController()
2535 if (!m_fullScreenWindowController)
2538 [m_fullScreenWindowController close];
2539 m_fullScreenWindowController = nullptr;
2543 NSView *WebViewImpl::fullScreenPlaceholderView()
2545 #if ENABLE(FULLSCREEN_API)
2546 if (m_fullScreenWindowController && [m_fullScreenWindowController isFullScreen])
2547 return [m_fullScreenWindowController webViewPlaceholder];
2552 NSWindow *WebViewImpl::fullScreenWindow()
2554 #if ENABLE(FULLSCREEN_API)
2555 return [[[WebCoreFullScreenWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] styleMask:(NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable) backing:NSBackingStoreBuffered defer:NO] autorelease];
2561 bool WebViewImpl::isEditable() const
2563 return m_page->isEditable();
2566 typedef HashMap<SEL, String> SelectorNameMap;
2568 // Map selectors into Editor command names.
2569 // This is not needed for any selectors that have the same name as the Editor command.
2570 static const SelectorNameMap& selectorExceptionMap()
2572 static NeverDestroyed<SelectorNameMap> map;
2574 struct SelectorAndCommandName {
2576 ASCIILiteral commandName;
2579 static const SelectorAndCommandName names[] = {
2580 { @selector(insertNewlineIgnoringFieldEditor:), "InsertNewline"_s },
2581 { @selector(insertParagraphSeparator:), "InsertNewline"_s },
2582 { @selector(insertTabIgnoringFieldEditor:), "InsertTab"_s },
2583 { @selector(pageDown:), "MovePageDown"_s },
2584 { @selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection"_s },
2585 { @selector(pageUp:), "MovePageUp"_s },
2586 { @selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection"_s },
2587 { @selector(scrollPageDown:), "ScrollPageForward"_s },
2588 { @selector(scrollPageUp:), "ScrollPageBackward"_s }
2591 for (auto& name : names)
2592 map.get().add(name.selector, name.commandName);
2597 static String commandNameForSelector(SEL selector)
2599 // Check the exception map first.
2600 static const SelectorNameMap& exceptionMap = selectorExceptionMap();
2601 SelectorNameMap::const_iterator it = exceptionMap.find(selector);
2602 if (it != exceptionMap.end())
2605 // Remove the trailing colon.
2606 // No need to capitalize the command name since Editor command names are
2607 // not case sensitive.
2608 const char* selectorName = sel_getName(selector);
2609 size_t selectorNameLength = strlen(selectorName);
2610 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2612 return String(selectorName, selectorNameLength - 1);
2615 bool WebViewImpl::executeSavedCommandBySelector(SEL selector)
2617 LOG(TextInput, "Executing previously saved command %s", sel_getName(selector));
2618 // The sink does two things: 1) Tells us if the responder went unhandled, and
2619 // 2) prevents any NSBeep; we don't ever want to beep here.
2620 RetainPtr<WKResponderChainSink> sink = adoptNS([[WKResponderChainSink alloc] initWithResponderChain:m_view.getAutoreleased()]);
2621 [m_view _web_superDoCommandBySelector:selector];
2623 return ![sink didReceiveUnhandledCommand];
2626 void WebViewImpl::executeEditCommandForSelector(SEL selector, const String& argument)
2628 m_page->executeEditCommand(commandNameForSelector(selector), argument);
2631 void WebViewImpl::registerEditCommand(Ref<WebEditCommandProxy>&& command, UndoOrRedo undoOrRedo)
2633 RetainPtr<WKEditCommandObjC> commandObjC = adoptNS([[WKEditCommandObjC alloc] initWithWebEditCommandProxy:command.ptr()]);
2634 String actionName = WebEditCommandProxy::nameForEditAction(command->editAction());
2636 NSUndoManager *undoManager = [m_view undoManager];
2637 [undoManager registerUndoWithTarget:m_undoTarget.get() selector:((undoOrRedo == UndoOrRedo::Undo) ? @selector(undoEditing:) : @selector(redoEditing:)) object:commandObjC.get()];
2638 if (!actionName.isEmpty())
2639 [undoManager setActionName:(NSString *)actionName];
2642 void WebViewImpl::clearAllEditCommands()
2644 [[m_view undoManager] removeAllActionsWithTarget:m_undoTarget.get()];
2647 bool WebViewImpl::writeSelectionToPasteboard(NSPasteboard *pasteboard, NSArray *types)
2649 size_t numTypes = types.count;
2650 [pasteboard declareTypes:types owner:nil];
2651 for (size_t i = 0; i < numTypes; ++i) {
2652 if ([[types objectAtIndex:i] isEqualTo:WebCore::legacyStringPasteboardType()])
2653 [pasteboard setString:m_page->stringSelectionForPasteboard() forType:WebCore::legacyStringPasteboardType()];
2655 RefPtr<WebCore::SharedBuffer> buffer = m_page->dataSelectionForPasteboard([types objectAtIndex:i]);
2656 [pasteboard setData:buffer ? buffer->createNSData().get() : nil forType:[types objectAtIndex:i]];
2662 bool WebViewImpl::readSelectionFromPasteboard(NSPasteboard *pasteboard)
2664 return m_page->readSelectionFromPasteboard([pasteboard name]);
2667 id WebViewImpl::validRequestorForSendAndReturnTypes(NSString *sendType, NSString *returnType)
2669 EditorState editorState = m_page->editorState();
2670 bool isValidSendType = false;
2672 if (sendType && !editorState.selectionIsNone) {
2673 if (editorState.isInPlugin)
2674 isValidSendType = [sendType isEqualToString:WebCore::legacyStringPasteboardType()];
2676 isValidSendType = [PasteboardTypes::forSelection() containsObject:sendType];
2679 bool isValidReturnType = false;
2681 isValidReturnType = true;
2682 else if ([PasteboardTypes::forEditing() containsObject:returnType] && editorState.isContentEditable) {
2683 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts.
2684 isValidReturnType = editorState.isContentRichlyEditable || [returnType isEqualToString:WebCore::legacyStringPasteboardType()];
2686 if (isValidSendType && isValidReturnType)
2687 return m_view.getAutoreleased();
2688 return [[m_view nextResponder] validRequestorForSendType:sendType returnType:returnType];
2691 void WebViewImpl::centerSelectionInVisibleArea()
2693 m_page->centerSelectionInVisibleArea();
2696 void WebViewImpl::selectionDidChange()
2698 updateFontPanelIfNeeded();
2699 if (!m_isHandlingAcceptedCandidate)
2700 m_softSpaceRange = NSMakeRange(NSNotFound, 0);
2703 if (!m_page->editorState().isMissingPostLayoutData)
2704 requestCandidatesForSelectionIfNeeded();
2707 [m_view _web_editorStateDidChange];
2710 void WebViewImpl::didBecomeEditable()
2712 [m_windowVisibilityObserver startObservingFontPanel];
2714 dispatch_async(dispatch_get_main_queue(), [] {
2715 [[NSSpellChecker sharedSpellChecker] _preflightChosenSpellServer];
2719 void WebViewImpl::updateFontPanelIfNeeded()
2721 const EditorState& editorState = m_page->editorState();
2722 if (editorState.selectionIsNone || !editorState.isContentEditable)
2724 if ([NSFontPanel sharedFontPanelExists] && [[NSFontPanel sharedFontPanel] isVisible]) {
2725 m_page->fontAtSelection([](const String& fontName, double fontSize, bool selectionHasMultipleFonts, WebKit::CallbackBase::Error error) {
2726 NSFont *font = [NSFont fontWithName:fontName size:fontSize];
2728 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:selectionHasMultipleFonts];
2733 void WebViewImpl::changeFontFromFontPanel()
2735 NSFontManager *fontManager = [NSFontManager sharedFontManager];
2736 NSFont *font = [fontManager convertFont:fontManager.selectedFont];
2739 m_page->setFont(font.familyName, font.pointSize, font.fontDescriptor.symbolicTraits);
2742 static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item)
2744 if (![(NSObject *)item isKindOfClass:[NSMenuItem class]])
2746 return (NSMenuItem *)item;
2749 static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item)
2751 if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]])
2753 return (NSToolbarItem *)item;
2756 bool WebViewImpl::validateUserInterfaceItem(id <NSValidatedUserInterfaceItem> item)
2758 ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
2759 SEL action = [item action];
2761 if (action == @selector(showGuessPanel:)) {
2762 if (NSMenuItem *menuItem = WebKit::menuItem(item))
2763 [menuItem setTitle:WebCore::contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])];
2764 return m_page->editorState().isContentEditable;
2767 if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:))
2768 return m_page->editorState().isContentEditable;
2770 if (action == @selector(toggleContinuousSpellChecking:)) {
2771 bool enabled = TextChecker::isContinuousSpellCheckingAllowed();
2772 bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled;
2773 [menuItem(item) setState:checked ? NSControlStateValueOn : NSControlStateValueOff];
2777 if (action == @selector(toggleGrammarChecking:)) {
2778 bool checked = TextChecker::state().isGrammarCheckingEnabled;
2779 [menuItem(item) setState:checked ? NSControlStateValueOn : NSControlStateValueOff];
2783 if (action == @selector(toggleAutomaticSpellingCorrection:)) {
2784 bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled;
2785 [menuItem(item) setState:checked ? NSControlStateValueOn : NSControlStateValueOff];
2786 return m_page->editorState().isContentEditable;
2789 if (action == @selector(orderFrontSubstitutionsPanel:)) {
2790 if (NSMenuItem *menuItem = WebKit::menuItem(item))
2791 [menuItem setTitle:WebCore::contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])];
2792 return m_page->editorState().isContentEditable;
2795 if (action == @selector(toggleSmartInsertDelete:)) {
2796 bool checked = m_page->isSmartInsertDeleteEnabled();
2797 [menuItem(item) setState:checked ? NSControlStateValueOn : NSControlStateValueOff];
2798 return m_page->editorState().isContentEditable;
2801 if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
2802 bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
2803 [menuItem(item) setState:checked ? NSControlStateValueOn : NSControlStateValueOff];
2804 return m_page->editorState().isContentEditable;
2807 if (action == @selector(toggleAutomaticDashSubstitution:)) {
2808 bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled;
2809 [menuItem(item) setState:checked ? NSControlStateValueOn : NSControlStateValueOff];
2810 return m_page->editorState().isContentEditable;
2813 if (action == @selector(toggleAutomaticLinkDetection:)) {
2814 bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled;
2815 [menuItem(item) setState:checked ? NSControlStateValueOn : NSControlStateValueOff];
2816 return m_page->editorState().isContentEditable;
2819 if (action == @selector(toggleAutomaticTextReplacement:)) {
2820 bool checked = TextChecker::state().isAutomaticTextReplacementEnabled;
2821 [menuItem(item) setState:checked ? NSControlStateValueOn : NSControlStateValueOff];
2822 return m_page->editorState().isContentEditable;
2825 if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:))
2826 return m_page->editorState().selectionIsRange && m_page->editorState().isContentEditable;
2828 if (action == @selector(stopSpeaking:))
2829 return [NSApp isSpeaking];
2831 // The centerSelectionInVisibleArea: selector is enabled if there's a selection range or if there's an insertion point in an editable area.
2832 if (action == @selector(centerSelectionInVisibleArea:))
2833 return m_page->editorState().selectionIsRange || (m_page->editorState().isContentEditable && !m_page->editorState().selectionIsNone);
2835 // Next, handle editor commands. Start by returning true for anything that is not an editor command.
2836 // Returning true is the default thing to do in an AppKit validate method for any selector that is not recognized.
2837 String commandName = commandNameForSelector([item action]);
2838 if (!WebCore::Editor::commandIsSupportedFromMenuOrKeyBinding(commandName))
2841 // Add this item to the vector of items for a given command that are awaiting validation.
2842 ValidationMap::AddResult addResult = m_validationMap.add(commandName, ValidationVector());
2843 addResult.iterator->value.append(item);
2844 if (addResult.isNewEntry) {
2845 // If we are not already awaiting validation for this command, start the asynchronous validation process.
2846 // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time
2847 // we asked for the same command; there is no guarantee the answer is still valid.
2848 auto weakThis = makeWeakPtr(*this);
2849 m_page->validateCommand(commandName, [weakThis](const String& commandName, bool isEnabled, int32_t state, WebKit::CallbackBase::Error error) {
2853 // If the process exits before the command can be validated, we'll be called back with an error.
2854 if (error != WebKit::CallbackBase::Error::None)
2857 weakThis->setUserInterfaceItemState(commandName, isEnabled, state);
2861 // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called.
2862 // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment.
2863 // But returning NO here would be worse; that would make keyboard commands such as command-C fail.
2867 void WebViewImpl::setUserInterfaceItemState(NSString *commandName, bool enabled, int state)
2869 ValidationVector items = m_validationMap.take(commandName);
2870 for (auto& item : items) {
2871 [menuItem(item.get()) setState:state];
2872 [menuItem(item.get()) setEnabled:enabled];
2873 [toolbarItem(item.get()) setEnabled:enabled];
2874 // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled.
2878 void WebViewImpl::startSpeaking()
2880 ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
2881 m_page->getSelectionOrContentsAsString([](const String& string, WebKit::CallbackBase::Error error) {
2882 if (error != WebKit::CallbackBase::Error::None)
2887 [NSApp speakString:string];
2891 void WebViewImpl::stopSpeaking(id sender)
2893 ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
2894 [NSApp stopSpeaking:sender];
2897 void WebViewImpl::showGuessPanel(id sender)
2899 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2901 LOG_ERROR("No NSSpellChecker");
2905 NSPanel *spellingPanel = [checker spellingPanel];
2906 if ([spellingPanel isVisible]) {
2907 [spellingPanel orderOut:sender];
2911 m_page->advanceToNextMisspelling(true);
2912 [spellingPanel orderFront:sender];
2915 void WebViewImpl::checkSpelling()
2917 m_page->advanceToNextMisspelling(false);
2920 void WebViewImpl::changeSpelling(id sender)
2922 NSString *word = [[sender selectedCell] stringValue];
2924 m_page->changeSpellingToWord(word);
2927 void WebViewImpl::toggleContinuousSpellChecking()
2929 bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled;
2930 TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled);
2932 m_page->process().updateTextCheckerState();
2935 bool WebViewImpl::isGrammarCheckingEnabled()
2937 return TextChecker::state().isGrammarCheckingEnabled;
2940 void WebViewImpl::setGrammarCheckingEnabled(bool flag)
2942 if (static_cast<bool>(flag) == TextChecker::state().isGrammarCheckingEnabled)
2945 TextChecker::setGrammarCheckingEnabled(flag);
2946 m_page->process().updateTextCheckerState();
2949 void WebViewImpl::toggleGrammarChecking()
2951 bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled;
2952 TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled);
2954 m_page->process().updateTextCheckerState();
2957 void WebViewImpl::toggleAutomaticSpellingCorrection()
2959 TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled);
2961 m_page->process().updateTextCheckerState();
2964 void WebViewImpl::orderFrontSubstitutionsPanel(id sender)
2966 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2968 LOG_ERROR("No NSSpellChecker");
2972 NSPanel *substitutionsPanel = [checker substitutionsPanel];
2973 if ([substitutionsPanel isVisible]) {
2974 [substitutionsPanel orderOut:sender];
2977 [substitutionsPanel orderFront:sender];
2980 void WebViewImpl::toggleSmartInsertDelete()
2982 m_page->setSmartInsertDeleteEnabled(!m_page->isSmartInsertDeleteEnabled());
2985 bool WebViewImpl::isAutomaticQuoteSubstitutionEnabled()
2987 return TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
2990 void WebViewImpl::setAutomaticQuoteSubstitutionEnabled(bool flag)
2992 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled)
2995 TextChecker::setAutomaticQuoteSubstitutionEnabled(flag);
2996 m_page->process().updateTextCheckerState();
2999 void WebViewImpl::toggleAutomaticQuoteSubstitution()
3001 TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled);
3002 m_page->process().updateTextCheckerState();
3005 bool WebViewImpl::isAutomaticDashSubstitutionEnabled()
3007 return TextChecker::state().isAutomaticDashSubstitutionEnabled;
3010 void WebViewImpl::setAutomaticDashSubstitutionEnabled(bool flag)
3012 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled)
3015 TextChecker::setAutomaticDashSubstitutionEnabled(flag);
3016 m_page->process().updateTextCheckerState();
3019 void WebViewImpl::toggleAutomaticDashSubstitution()
3021 TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled);
3022 m_page->process().updateTextCheckerState();
3025 bool WebViewImpl::isAutomaticLinkDetectionEnabled()
3027 return TextChecker::state().isAutomaticLinkDetectionEnabled;
3030 void WebViewImpl::setAutomaticLinkDetectionEnabled(bool flag)
3032 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled)
3035 TextChecker::setAutomaticLinkDetectionEnabled(flag);
3036 m_page->process().updateTextCheckerState();
3039 void WebViewImpl::toggleAutomaticLinkDetection()
3041 TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled);
3042 m_page->process().updateTextCheckerState();
3045 bool WebViewImpl::isAutomaticTextReplacementEnabled()
3047 return TextChecker::state().isAutomaticTextReplacementEnabled;
3050 void WebViewImpl::setAutomaticTextReplacementEnabled(bool flag)
3052 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled)
3055 TextChecker::setAutomaticTextReplacementEnabled(flag);
3056 m_page->process().updateTextCheckerState();
3059 void WebViewImpl::toggleAutomaticTextReplacement()
3061 TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled);
3062 m_page->process().updateTextCheckerState();
3065 void WebViewImpl::uppercaseWord()
3067 m_page->uppercaseWord();
3070 void WebViewImpl::lowercaseWord()
3072 m_page->lowercaseWord();
3075 void WebViewImpl::capitalizeWord()
3077 m_page->capitalizeWord();
3080 void WebViewImpl::requestCandidatesForSelectionIfNeeded()
3082 if (!shouldRequestCandidates())
3085 const EditorState& editorState = m_page->editorState();
3086 if (!editorState.isContentEditable)
3089 if (editorState.isMissingPostLayoutData)
3092 auto& postLayoutData = editorState.postLayoutData();
3093 m_lastStringForCandidateRequest = postLayoutData.stringForCandidateRequest;
3095 #if HAVE(ADVANCED_SPELL_CHECKING)
3096 NSRange selectedRange = NSMakeRange(postLayoutData.candidateRequestStartPosition, postLayoutData.selectedTextLength);
3097 NSTextCheckingTypes checkingTypes = NSTextCheckingTypeSpelling | NSTextCheckingTypeReplacement | NSTextCheckingTypeCorrection;
3098 auto weakThis = makeWeakPtr(*this);
3099 m_lastCandidateRequestSequenceNumber = [[NSSpellChecker sharedSpellChecker] requestCandidatesForSelectedRange:selectedRange inString:postLayoutData.paragraphContextForCandidateRequest types:checkingTypes options:nil inSpellDocumentWithTag:spellCheckerDocumentTag() completionHandler:[weakThis](NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates) {
3100 dispatch_async(dispatch_get_main_queue(), ^{
3103 weakThis->handleRequestedCandidates(sequenceNumber, candidates);
3106 #endif // HAVE(ADVANCED_SPELL_CHECKING)
3109 void WebViewImpl::handleRequestedCandidates(NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates)
3111 if (!shouldRequestCandidates())
3114 if (m_lastCandidateRequestSequenceNumber != sequenceNumber)
3117 const EditorState& editorState = m_page->editorState();
3118 if (!editorState.isContentEditable)
3121 // FIXME: It's pretty lame that we have to depend on the most recent EditorState having post layout data,
3122 // and that we just bail if it is missing.
3123 if (editorState.isMissingPostLayoutData)
3126 auto& postLayoutData = editorState.postLayoutData();
3127 if (m_lastStringForCandidateRequest != postLayoutData.stringForCandidateRequest)
3131 NSRange selectedRange = NSMakeRange(postLayoutData.candidateRequestStartPosition, postLayoutData.selectedTextLength);
3132 WebCore::IntRect offsetSelectionRect = postLayoutData.selectionClipRect;
3133 offsetSelectionRect.move(0, offsetSelectionRect.height());
3135 [candidateListTouchBarItem() setCandidates:candidates forSelectedRange:selectedRange inString:postLayoutData.paragraphContextForCandidateRequest rect:offsetSelectionRect view:m_view.getAutoreleased() completionHandler:nil];
3137 UNUSED_PARAM(candidates);
3141 static constexpr WebCore::TextCheckingType coreTextCheckingType(NSTextCheckingType type)
3144 case NSTextCheckingTypeCorrection:
3145 return WebCore::TextCheckingType::Correction;
3146 case NSTextCheckingTypeReplacement:
3147 return WebCore::TextCheckingType::Replacement;
3148 case NSTextCheckingTypeSpelling:
3149 return WebCore::TextCheckingType::Spelling;
3151 return WebCore::TextCheckingType::None;
3155 static WebCore::TextCheckingResult textCheckingResultFromNSTextCheckingResult(NSTextCheckingResult *nsResult)
3157 NSRange resultRange = [nsResult range];
3159 WebCore::TextCheckingResult result;
3160 result.type = coreTextCheckingType(nsResult.resultType);
3161 result.location = resultRange.location;
3162 result.length = resultRange.length;
3163 result.replacement = nsResult.replacementString;
3167 void WebViewImpl::handleAcceptedCandidate(NSTextCheckingResult *acceptedCandidate)
3169 const EditorState& editorState = m_page->editorState();
3170 if (!editorState.isContentEditable)
3173 // FIXME: It's pretty lame that we have to depend on the most recent EditorState having post layout data,
3174 // and that we just bail if it is missing.
3175 if (editorState.isMissingPostLayoutData)
3178 auto& postLayoutData = editorState.postLayoutData();
3179 if (m_lastStringForCandidateRequest != postLayoutData.stringForCandidateRequest)
3182 m_isHandlingAcceptedCandidate = true;
3183 NSRange range = [acceptedCandidate range];
3184 if (acceptedCandidate.replacementString && [acceptedCandidate.replacementString length] > 0) {
3185 NSRange replacedRange = NSMakeRange(range.location, [acceptedCandidate.replacementString length]);
3186 NSRange softSpaceRange = NSMakeRange(NSMaxRange(replacedRange) - 1, 1);
3187 if ([acceptedCandidate.replacementString hasSuffix:@" "])
3188 m_softSpaceRange = softSpaceRange;
3191 m_page->handleAcceptedCandidate(textCheckingResultFromNSTextCheckingResult(acceptedCandidate));
3194 void WebViewImpl::doAfterProcessingAllPendingMouseEvents(dispatch_block_t action)
3196 if (!m_page->isProcessingMouseEvents()) {
3201 m_callbackHandlersAfterProcessingPendingMouseEvents.append(makeBlockPtr(action));
3204 void WebViewImpl::didFinishProcessingAllPendingMouseEvents()
3206 flushPendingMouseEventCallbacks();
3209 void WebViewImpl::flushPendingMouseEventCallbacks()
3211 for (auto& callback : m_callbackHandlersAfterProcessingPendingMouseEvents)
3214 m_callbackHandlersAfterProcessingPendingMouseEvents.clear();
3217 void WebViewImpl::preferencesDidChange()
3219 BOOL needsViewFrameInWindowCoordinates = m_page->preferences().pluginsEnabled();
3221 if (!!needsViewFrameInWindowCoordinates == !!m_needsViewFrameInWindowCoordinates)
3224 m_needsViewFrameInWindowCoordinates = needsViewFrameInWindowCoordinates;
3225 if ([m_view window])
3226 updateWindowAndViewFrames();
3229 void WebViewImpl::setTextIndicator(WebCore::TextIndicator& textIndicator, WebCore::TextIndicatorWindowLifetime lifetime)
3231 if (!m_textIndicatorWindow)
3232 m_textIndicatorWindow = std::make_unique<WebCore::TextIndicatorWindow>(m_view.getAutoreleased());
3234 NSRect textBoundingRectInScreenCoordinates = [[m_view window] convertRectToScreen:[m_view convertRect:textIndicator.textBoundingRectInRootViewCoordinates() toView:nil]];
3235 m_textIndicatorWindow->setTextIndicator(textIndicator, NSRectToCGRect(textBoundingRectInScreenCoordinates), lifetime);
3238 void WebViewImpl::clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation animation)
3240 if (m_textIndicatorWindow)
3241 m_textIndicatorWindow->clearTextIndicator(animation);
3242 m_textIndicatorWindow = nullptr;
3245 void WebViewImpl::setTextIndicatorAnimationProgress(float progress)
3247 if (m_textIndicatorWindow)
3248 m_textIndicatorWindow->setAnimationProgress(progress);
3251 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimation(bool animate)
3253 [m_view _web_dismissContentRelativeChildWindowsWithAnimation:animate];
3256 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimationFromViewOnly(bool animate)
3258 // Calling _clearTextIndicatorWithAnimation here will win out over the animated clear in dismissContentRelativeChildWindowsFromViewOnly.
3259 // We can't invert these because clients can override (and have overridden) _dismissContentRelativeChildWindows, so it needs to be called.
3260 // For this same reason, this can't be moved to WebViewImpl without care.
3261 clearTextIndicatorWithAnimation(animate ? WebCore::TextIndicatorWindowDismissalAnimation::FadeOut : WebCore::TextIndicatorWindowDismissalAnimation::None);
3262 [m_view _web_dismissContentRelativeChildWindows];
3265 void WebViewImpl::dismissContentRelativeChildWindowsFromViewOnly()
3267 bool hasActiveImmediateAction = false;
3268 hasActiveImmediateAction = [m_immediateActionController hasActiveImmediateAction];
3270 // FIXME: We don't know which panel we are dismissing, it may not even be in the current page (see <rdar://problem/13875766>).
3271 if ([m_view window].isKeyWindow || hasActiveImmediateAction) {
3272 WebCore::DictionaryLookup::hidePopup();
3274 if (DataDetectorsLibrary())
3275 [[getDDActionsManagerClass() sharedManager] requestBubbleClosureUnanchorOnFailure:YES];
3278 clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::FadeOut);
3280 [m_immediateActionController dismissContentRelativeChildWindows];
3282 m_pageClient->dismissCorrectionPanel(WebCore::ReasonForDismissingAlternativeTextIgnored);
3285 void WebViewImpl::hideWordDefinitionWindow()
3287 WebCore::DictionaryLookup::hidePopup();
3290 void WebViewImpl::quickLookWithEvent(NSEvent *event)
3292 if (ignoresNonWheelEvents())
3295 if (m_immediateActionGestureRecognizer) {
3296 [m_view _web_superQuickLookWithEvent:event];
3300 NSPoint locationInViewCoordinates = [m_view convertPoint:[event locationInWindow] fromView:nil];
3301 m_page->performDictionaryLookupAtLocation(WebCore::FloatPoint(locationInViewCoordinates));
3304 void WebViewImpl::prepareForDictionaryLookup()
3306 [m_windowVisibilityObserver startObservingLookupDismissalIfNeeded];
3309 void WebViewImpl::setAllowsLinkPreview(bool allowsLinkPreview)
3311 if (m_allowsLinkPreview == allowsLinkPreview)
3314 m_allowsLinkPreview = allowsLinkPreview;
3316 if (!allowsLinkPreview)
3317 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
3318 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get())
3319 [m_view addGestureRecognizer:immediateActionRecognizer];
3322 void* WebViewImpl::immediateActionAnimationControllerForHitTestResult(API::HitTestResult* hitTestResult, uint32_t type, API::Object* userData)
3324 return [m_view _web_immediateActionAnimationControllerForHitTestResultInternal:hitTestResult withType:type userData:userData];
3327 void WebViewImpl::didPerformImmediateActionHitTest(const WebHitTestResultData& result, bool contentPreventsDefault, API::Object* userData)
3329 [m_immediateActionController didPerformImmediateActionHitTest:result contentPreventsDefault:contentPreventsDefault userData:userData];
3332 void WebViewImpl::prepareForImmediateActionAnimation()
3334 [m_view _web_prepareForImmediateActionAnimation];
3337 void WebViewImpl::cancelImmediateActionAnimation()
3339 [m_view _web_cancelImmediateActionAnimation];
3342 void WebViewImpl::completeImmediateActionAnimation()
3344 [m_view _web_completeImmediateActionAnimation];
3347 void WebViewImpl::didChangeContentSize(CGSize newSize)
3349 [m_view _web_didChangeContentSize:NSSizeFromCGSize(newSize)];
3352 void WebViewImpl::didHandleAcceptedCandidate()
3354 m_isHandlingAcceptedCandidate = false;
3356 [m_view _didHandleAcceptedCandidate];
3359 void WebViewImpl::videoControlsManagerDidChange()
3365 #if ENABLE(FULLSCREEN_API)
3366 if (hasFullScreenWindowController())
3367 [fullScreenWindowController() videoControlsManagerDidChange];
3371 void WebViewImpl::setIgnoresNonWheelEvents(bool ignoresNonWheelEvents)
3373 if (m_ignoresNonWheelEvents == ignoresNonWheelEvents)
3376 m_ignoresNonWheelEvents = ignoresNonWheelEvents;
3377 m_page->setShouldDispatchFakeMouseMoveEvents(!ignoresNonWheelEvents);
3379 if (ignoresNonWheelEvents)
3380 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
3381 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get()) {
3382 if (m_allowsLinkPreview)
3383 [m_view addGestureRecognizer:immediateActionRecognizer];
3387 void WebViewImpl::setIgnoresAllEvents(bool ignoresAllEvents)
3389 m_ignoresAllEvents = ignoresAllEvents;
3390 setIgnoresNonWheelEvents(ignoresAllEvents);
3393 void WebViewImpl::setIgnoresMouseDraggedEvents(bool ignoresMouseDraggedEvents)
3395 m_ignoresMouseDraggedEvents = ignoresMouseDraggedEvents;
3398 void WebViewImpl::setAccessibilityWebProcessToken(NSData *data)
3400 m_remoteAccessibilityChild = data.length ? adoptNS([[NSAccessibilityRemoteUIElement alloc] initWithRemoteToken:data]) : nil;
3401 updateRemoteAccessibilityRegistration(true);
3404 void WebViewImpl::updateRemoteAccessibilityRegistration(bool registerProcess)
3406 // When the tree is connected/disconnected, the remote accessibility registration
3407 // needs to be updated with the pid of the remote process. If the process is going
3408 // away, that information is not present in WebProcess
3410 if (registerProcess)
3411 pid = m_page->process().processIdentifier();
3412 else if (!registerProcess) {
3413 pid = [m_remoteAccessibilityChild processIdentifier];
3414 m_remoteAccessibilityChild = nil;
3419 if (registerProcess)
3420 [NSAccessibilityRemoteUIElement registerRemoteUIProcessIdentifier:pid];
3422 [NSAccessibilityRemoteUIElement unregisterRemoteUIProcessIdentifier:pid];
3425 void WebViewImpl::accessibilityRegisterUIProcessTokens()
3427 // Initialize remote accessibility when the window connection has been established.
3428 NSData *remoteElementToken = [NSAccessibilityRemoteUIElement remoteTokenForLocalUIElement:m_view.getAutoreleased()];
3429 NSData *remoteWindowToken = [NSAccessibilityRemoteUIElement remoteTokenForLocalUIElement:[m_view window]];
3430 IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
3431 IPC::DataReference windowToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]);
3432 m_page->registerUIProcessAccessibilityTokens(elementToken, windowToken);
3435 id WebViewImpl::accessibilityFocusedUIElement()
3437 enableAccessibilityIfNecessary();
3438 return m_remoteAccessibilityChild.get();
3441 id WebViewImpl::accessibilityHitTest(CGPoint)
3443 return accessibilityFocusedUIElement();
3446 void WebViewImpl::enableAccessibilityIfNecessary()
3448 if (WebCore::AXObjectCache::accessibilityEnabled())
3451 // After enabling accessibility update the window frame on the web process so that the
3452 // correct accessibility position is transmitted (when AX is off, that position is not calculated).
3453 WebCore::AXObjectCache::enableAccessibility();
3454 updateWindowAndViewFrames();
3457 id WebViewImpl::accessibilityAttributeValue(NSString *attribute)
3459 enableAccessibilityIfNecessary();
3461 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
3464 if (m_remoteAccessibilityChild)
3465 child = m_remoteAccessibilityChild.get();
3469 return [NSArray arrayWithObject:child];
3471 if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
3472 return NSAccessibilityGroupRole;
3473 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
3474 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
3475 if ([attribute isEqualToString:NSAccessibilityParentAttribute])
3476 return NSAccessibilityUnignoredAncestor([m_view superview]);
3477 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
3480 return [m_view _web_superAccessibilityAttributeValue:attribute];
3483 void WebViewImpl::setPrimaryTrackingArea(NSTrackingArea *trackingArea)
3485 [m_view removeTrackingArea:m_primaryTrackingArea.get()];
3486 m_primaryTrackingArea = trackingArea;
3487 [m_view addTrackingArea:trackingArea];
3490 // Any non-zero value will do, but using something recognizable might help us debug some day.
3491 #define TRACKING_RECT_TAG 0xBADFACE
3493 NSTrackingRectTag WebViewImpl::addTrackingRect(CGRect, id owner, void* userData, bool assumeInside)
3495 ASSERT(m_trackingRectOwner == nil);
3496 m_trackingRectOwner = owner;
3497 m_trackingRectUserData = userData;
3498 return TRACKING_RECT_TAG;
3501 NSTrackingRectTag WebViewImpl::addTrackingRectWithTrackingNum(CGRect, id owner, void* userData, bool assumeInside, int tag)
3503 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
3504 ASSERT(m_trackingRectOwner == nil);
3505 m_trackingRectOwner = owner;
3506 m_trackingRectUserData = userData;
3507 return TRACKING_RECT_TAG;
3510 void WebViewImpl::addTrackingRectsWithTrackingNums(CGRect*, id owner, void** userDataList, bool assumeInside, NSTrackingRectTag *trackingNums, int count)
3513 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
3514 ASSERT(m_trackingRectOwner == nil);
3515 m_trackingRectOwner = owner;
3516 m_trackingRectUserData = userDataList[0];
3517 trackingNums[0] = TRACKING_RECT_TAG;
3520 void WebViewImpl::removeTrackingRect(NSTrackingRectTag tag)
3525 if (tag == TRACKING_RECT_TAG) {
3526 m_trackingRectOwner = nil;
3530 if (tag == m_lastToolTipTag) {
3531 [m_view _web_superRemoveTrackingRect:tag];
3532 m_lastToolTipTag = 0;
3536 // If any other tracking rect is being removed, we don't know how it was created
3537 // and it's possible there's a leak involved (see 3500217)
3538 ASSERT_NOT_REACHED();
3541 void WebViewImpl::removeTrackingRects(NSTrackingRectTag *tags, int count)
3543 for (int i = 0; i < count; ++i) {
3547 ASSERT(tag == TRACKING_RECT_TAG);
3548 m_trackingRectOwner = nil;
3552 void WebViewImpl::sendToolTipMouseExited()
3554 // Nothing matters except window, trackingNumber, and userData.
3555 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSEventTypeMouseExited
3556 location:NSMakePoint(0, 0)
3559 windowNumber:[m_view window].windowNumber
3562 trackingNumber:TRACKING_RECT_TAG
3563 userData:m_trackingRectUserData];
3564 [m_trackingRectOwner mouseExited:fakeEvent];
3567 void WebViewImpl::sendToolTipMouseEntered()
3569 // Nothing matters except window, trackingNumber, and userData.
3570 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSEventTypeMouseEntered
3571 location:NSMakePoint(0, 0)
3574 windowNumber:[m_view window].windowNumber
3577 trackingNumber:TRACKING_RECT_TAG
3578 userData:m_trackingRectUserData];
3579 [m_trackingRectOwner mouseEntered:fakeEvent];
3582 NSString *WebViewImpl::stringForToolTip(NSToolTipTag tag)
3584 return nsStringFromWebCoreString(m_page->toolTip());
3587 void WebViewImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip)
3589 if (!oldToolTip.isNull())
3590 sendToolTipMouseExited();
3592 if (!newToolTip.isEmpty()) {
3593 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
3594 [m_view removeAllToolTips];
3595 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
3596 m_lastToolTipTag = [m_view addToolTipRect:wideOpenRect owner:m_view.getAutoreleased() userData:NULL];
3597 sendToolTipMouseEntered();
3601 void WebViewImpl::setAcceleratedCompositingRootLayer(CALayer *rootLayer)
3603 [rootLayer web_disableAllActions];
3605 m_rootLayer = rootLayer;
3608 if (m_thumbnailView) {
3609 updateThumbnailViewLayer();
3614 [CATransaction begin];
3615 [CATransaction setDisableActions:YES];
3618 if (!m_layerHostingView) {
3619 // Create an NSView that will host our layer tree.
3620 m_layerHostingView = adoptNS([[WKFlippedView alloc] initWithFrame:[m_view bounds]]);
3621 [m_layerHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
3623 [m_view addSubview:m_layerHostingView.get() positioned:NSWindowBelow relativeTo:nil];
3625 // Create a root layer that will back the NSView.
3626 RetainPtr<CALayer> layer = adoptNS([[CALayer alloc] init]);
3627 [layer setDelegate:[WebActionDisablingCALayerDelegate shared]];
3629 [layer setName:@"Hosting root layer"];
3632 [m_layerHostingView setLayer:layer.get()];
3633 [m_layerHostingView setWantsLayer:YES];
3636 [m_layerHostingView layer].sublayers = [NSArray arrayWithObject:rootLayer];
3637 } else if (m_layerHostingView) {
3638 [m_layerHostingView removeFromSuperview];
3639 [m_layerHostingView setLayer:nil];
3640 [m_layerHostingView setWantsLayer:NO];
3642 m_layerHostingView = nullptr;
3645 [CATransaction commit];
3649 void WebViewImpl::setThumbnailView(_WKThumbnailView *thumbnailView)
3651 ASSERT(!m_thumbnailView || !thumbnailView);
3653 m_thumbnailView = thumbnailView;
3656 updateThumbnailViewLayer();
3658 setAcceleratedCompositingRootLayer(m_rootLayer.get());
3661 void WebViewImpl::reparentLayerTreeInThumbnailView()
3663 m_thumbnailView._thumbnailLayer = m_rootLayer.get();
3666 void WebViewImpl::updateThumbnailViewLayer()
3668 _WKThumbnailView *thumbnailView = m_thumbnailView;
3669 ASSERT(thumbnailView);
3671 if (thumbnailView._waitingForSnapshot && [m_view window])
3672 reparentLayerTreeInThumbnailView();
3675 void WebViewImpl::setInspectorAttachmentView(NSView *newView)
3677 NSView *oldView = m_inspectorAttachmentView.get();
3678 if (oldView == newView)
3681 m_inspectorAttachmentView = newView;
3682 m_page->inspector()->attachmentViewDidChange(oldView ? oldView : m_view.getAutoreleased(), newView ? newView : m_view.getAutoreleased());
3685 NSView *WebViewImpl::inspectorAttachmentView()
3687 NSView *attachmentView = m_inspectorAttachmentView.get();
3688 return attachmentView ? attachmentView : m_view.getAutoreleased();
3691 _WKRemoteObjectRegistry *WebViewImpl::remoteObjectRegistry()
3693 if (!m_remoteObjectRegistry) {
3694 m_remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithWebPageProxy:m_page]);
3695 m_page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID(), [m_remoteObjectRegistry remoteObjectRegistry]);
3698 return m_remoteObjectRegistry.get();
3701 WKBrowsingContextController *WebViewImpl::browsingContextController()
3703 if (!m_browsingContextController)
3704 m_browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(m_page.ptr())]);
3706 return m_browsingContextController.get();
3708 #endif // WK_API_ENABLED
3710 #if ENABLE(DRAG_SUPPORT)
3711 void WebViewImpl::draggedImage(NSImage *, CGPoint endPoint, NSDragOperation operation)
3713 sendDragEndToPage(endPoint, operation);
3716 void WebViewImpl::sendDragEndToPage(CGPoint endPoint, NSDragOperation operation)
3718 #pragma clang diagnostic push
3719 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3720 NSPoint windowImageLoc = [[m_view window] convertScreenToBase:NSPointFromCGPoint(endPoint)];
3721 #pragma clang diagnostic pop
3722 NSPoint windowMouseLoc = windowImageLoc;
3724 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3725 m_ignoresMouseDraggedEvents = true;
3727 m_page->dragEnded(WebCore::IntPoint(windowMouseLoc), WebCore::IntPoint(WebCore::globalPoint(windowMouseLoc, [m_view window])), operation);
3730 static WebCore::DragApplicationFlags applicationFlagsForDrag(NSView *view, id <NSDraggingInfo> draggingInfo)
3732 ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
3734 if ([NSApp modalWindow])
3735 flags = WebCore::DragApplicationIsModal;
3736 if (view.window.attachedSheet)
3737 flags |= WebCore::DragApplicationHasAttachedSheet;
3738 if (draggingInfo.draggingSource == view)
3739 flags |= WebCore::DragApplicationIsSource;
3740 if ([NSApp currentEvent].modifierFlags & NSEventModifierFlagOption)
3741 flags |= WebCore::DragApplicationIsCopyKeyDown;
3742 return static_cast<WebCore::DragApplicationFlags>(flags);
3746 NSDragOperation WebViewImpl::draggingEntered(id <NSDraggingInfo> draggingInfo)
3748 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
3749 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, [m_view window]));
3751 auto dragDestinationAction = static_cast<WebCore::DragDestinationAction>([m_view _web_dragDestinationActionForDraggingInfo:draggingInfo]);
3753 auto dragDestinationAction = WebCore::DragDestinationActionAny;
3755 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view.getAutoreleased(), draggingInfo), dragDestinationAction);
3757 m_page->resetCurrentDragInformation();
3758 m_page->dragEntered(dragData, draggingInfo.draggingPasteboard.name);
3759 m_initialNumberOfValidItemsForDrop = draggingInfo.numberOfValidItemsForDrop;
3760 return NSDragOperationCopy;
3763 NSDragOperation WebViewImpl::draggingUpdated(id <NSDraggingInfo> draggingInfo)
3765 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
3766 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, [m_view window]));
3768 auto dragDestinationAction = static_cast<WebCore::DragDestinationAction>([m_view _web_dragDestinationActionForDraggingInfo:draggingInfo]);
3770 auto dragDestinationAction = WebCore::DragDestinationActionAny;
3772 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view.getAutoreleased(), draggingInfo), dragDestinationAction);
3773 m_page->dragUpdated(dragData, draggingInfo.draggingPasteboard.name);
3775 NSInteger numberOfValidItemsForDrop = m_page->currentDragNumberOfFilesToBeAccepted();
3777 if (m_page->currentDragOperation() == WebCore::DragOperationNone)
3778 numberOfValidItemsForDrop = m_initialNumberOfValidItemsForDrop;
3780 NSDraggingFormation draggingFormation = NSDraggingFormationNone;
3781 if (m_page->currentDragIsOverFileInput() && numberOfValidItemsForDrop > 0)
3782 draggingFormation = NSDraggingFormationList;
3784 if (draggingInfo.numberOfValidItemsForDrop != numberOfValidItemsForDrop)
3785 [draggingInfo setNumberOfValidItemsForDrop:numberOfValidItemsForDrop];
3786 if (draggingInfo.draggingFormation != draggingFormation)
3787 [draggingInfo setDraggingFormation:draggingFormation];
3789 return m_page->currentDragOperation();
3792 void WebViewImpl::draggingExited(id <NSDraggingInfo> draggingInfo)
3794 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
3795 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, [m_view window]));
3796 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view.getAutoreleased(), draggingInfo));
3797 m_page->dragExited(dragData, draggingInfo.draggingPasteboard.name);
3798 m_page->resetCurrentDragInformation();
3799 draggingInfo.numberOfValidItemsForDrop = m_initialNumberOfValidItemsForDrop;
3800 m_initialNumberOfValidItemsForDrop = 0;
3803 bool WebViewImpl::prepareForDragOperation(id <NSDraggingInfo>)
3808 bool WebViewImpl::performDragOperation(id <NSDraggingInfo> draggingInfo)
3810 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
3811 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, [m_view window]));
3812 WebCore::DragData *dragData = new WebCore::DragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view.getAutoreleased(), draggingInfo));
3814 NSArray *types = draggingInfo.draggingPasteboard.types;
3815 SandboxExtension::Handle sandboxExtensionHandle;
3816 SandboxExtension::HandleArray sandboxExtensionForUpload;
3818 if (![types containsObject:PasteboardTypes::WebArchivePboardType] && [types containsObject:WebCore::legacyFilesPromisePasteboardType()]) {
3820 // FIXME: legacyFilesPromisePasteboardType() contains UTIs, not path names. Also, it's not
3821 // guaranteed that the count of UTIs equals the count of files, since some clients only write
3823 NSArray *files = [draggingInfo.draggingPasteboard propertyListForType:WebCore::legacyFilesPromisePasteboardType()];
3824 if (![files isKindOfClass:[NSArray class]]) {
3829 NSString *dropDestinationPath = WebCore::FileSystem::createTemporaryDirectory(@"WebKitDropDestination");
3830 if (!dropDestinationPath) {
3835 size_t fileCount = files.count;
3836 Vector<String> *fileNames = new Vector<String>;
3837 NSURL *dropDestination = [NSURL fileURLWithPath:dropDestinationPath isDirectory:YES];
3838 String pasteboardName = draggingInfo.draggingPasteboard.name;
3839 [draggingInfo enumerateDraggingItemsWithOptions:0 forView:m_view.getAutoreleased() classes:@[[NSFilePromiseReceiver class]] searchOptions:@{ } usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) {
3840 NSFilePromiseReceiver *item = draggingItem.item;
3841 NSDictionary *options = @{ };
3843 RetainPtr<NSOperationQueue> queue = adoptNS([NSOperationQueue new]);
3844 [item receivePromisedFilesAtDestination:dropDestination options:options operationQueue:queue.get() reader:^(NSURL *fileURL, NSError *errorOrNil) {
3848 dispatch_async(dispatch_get_main_queue(), [this, path = RetainPtr<NSString>(fileURL.path), fileNames, fileCount, dragData, pasteboardName] {
3849 fileNames->append(path.get());
3850 if (fileNames->size() == fileCount) {
3851 SandboxExtension::Handle sandboxExtensionHandle;
3852 SandboxExtension::HandleArray sandboxExtensionForUpload;
3854 m_page->createSandboxExtensionsIfNeeded(*fileNames, sandboxExtensionHandle, sandboxExtensionForUpload);
3855 dragData->setFileNames(*fileNames);
3856 m_page->performDragOperation(*dragData, pasteboardName, WTFMove(sandboxExtensionHandle), WTFMove(sandboxExtensionForUpload));
3867 if ([types containsObject:WebCore::legacyFilenamesPasteboardType()]) {
3868 NSArray *files = [draggingInfo.draggingPasteboard propertyListForType:WebCore::legacyFilenamesPasteboardType()];
3869 if (![files isKindOfClass:[NSArray class]]) {
3874 Vector<String> fileNames;
3876 for (NSString *file in files)
3877 fileNames.append(file);
3878 m_page->createSandboxExtensionsIfNeeded(fileNames, sandboxExtensionHandle, sandboxExtensionForUpload);
3881 m_page->performDragOperation(*dragData, draggingInfo.draggingPasteboard.name, WTFMove(sandboxExtensionHandle), WTFMove(sandboxExtensionForUpload));
3887 NSView *WebViewImpl::hitTestForDragTypes(CGPoint point, NSSet *types)
3889 // This code is needed to support drag and drop when the drag types cannot be matched.
3890 // This is the case for elements that do not place content
3891 // in the drag pasteboard automatically when the drag start (i.e. dragging a DIV element).
3892 if ([[m_view superview] mouse:NSPointFromCGPoint(point) inRect:[m_view frame]])
3893 return m_view.getAutoreleased();
3897 void WebViewImpl::registerDraggedTypes()
3899 auto types = adoptNS([[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()]);
3900 [types addObjectsFromArray:PasteboardTypes::forURL()];
3901 [types addObject:PasteboardTypes::WebDummyPboardType];
3902 [m_view registerForDraggedTypes:[types allObjects]];
3905 NSString *WebViewImpl::fileNameForFilePromiseProvider(NSFilePromiseProvider *provider, NSString *)
3907 id userInfo = provider.userInfo;
3908 if (![userInfo isKindOfClass:[WKPromisedAttachmentContext class]])
3911 return [(WKPromisedAttachmentContext *)userInfo fileName];
3914 static NSError *webKitUnknownError()
3917 return [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nil];
3919 return [NSError errorWithDomain:@"WKErrorDomain" code:1 userInfo:nil];
3923 void WebViewImpl::didPerformDragOperation(bool handled)
3926 [m_view _web_didPerformDragOperation:handled];
3928 UNUSED_PARAM(handled);
3932 void WebViewImpl::writeToURLForFilePromiseProvider(NSFilePromiseProvider *provider, NSURL *fileURL, void(^completionHandler)(NSError *))
3934 id userInfo = provider.userInfo;
3935 if (![userInfo isKindOfClass:[WKPromisedAttachmentContext class]]) {
3936 completionHandler(webKitUnknownError());
3940 WKPromisedAttachmentContext *info = (WKPromisedAttachmentContext *)userInfo;
3941 auto attachment = m_page->attachmentForIdentifier(info.attachmentIdentifier);
3942 if (NSFileWrapper *fileWrapper = attachment ? attachment->fileWrapper() : nil) {
3943 NSError *attachmentWritingError = nil;
3944 if ([fileWrapper writeToURL:fileURL options:0 originalContentsURL:nil error:&attachmentWritingError])
3945 completionHandler(nil);
3947 completionHandler(attachmentWritingError);
3951 WebCore::URL blobURL { info.blobURL };
3952 if (blobURL.isEmpty()) {
3953 completionHandler(webKitUnknownError());
3957 m_page->writeBlobToFilePath(blobURL, fileURL.path, [protectedCompletionHandler = makeBlockPtr(completionHandler)] (bool success) {
3958 protectedCompletionHandler(success ? nil : webKitUnknownError());
3962 NSDragOperation WebViewImpl::dragSourceOperationMask(NSDraggingSession *, NSDraggingContext context)
3964 if (context == NSDraggingContextOutsideApplication || m_page->currentDragIsOverFileInput())
3965 return NSDragOperationCopy;
3966 return NSDragOperationGeneric | NSDragOperationMove | NSDragOperationCopy;
3969 void WebViewImpl::draggingSessionEnded(NSDraggingSession *, NSPoint endPoint, NSDragOperation operation)
3971 sendDragEndToPage(NSPointToCGPoint(endPoint), operation);
3974 #endif // ENABLE(DRAG_SUPPORT)
3976 void WebViewImpl::startWindowDrag()
3978 [[m_view window] performWindowDragWithEvent:m_lastMouseDownEvent.get()];
3981 void WebViewImpl::startDrag(const WebCore::DragItem& item, const ShareableBitmap::Handle& dragImageHandle)
3983 auto dragImageAsBitmap = ShareableBitmap::create(dragImageHandle);
3984 if (!dragImageAsBitmap) {
3985 m_page->dragCancelled();
3989 auto dragCGImage = dragImageAsBitmap->makeCGImage();
3990 auto dragNSImage = adoptNS([[NSImage alloc] initWithCGImage:dragCGImage.get() size:dragImageAsBitmap->size()]);
3992 WebCore::IntSize size([dragNSImage size]);
3993 size.scale(1.0 / m_page->deviceScaleFactor());
3994 [dragNSImage setSize:size];
3996 // The call below could release the view.
3997 auto protector = m_view.get();
3998 auto clientDragLocation = item.dragLocationInWindowCoordinates;
3999 #pragma clang diagnostic push
4000 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4001 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
4002 #pragma clang diagnostic pop
4004 if (auto& info = item.promisedAttachmentInfo) {
4005 NSString *utiType = info.contentType;
4006 NSString *fileName = info.fileName;
4007 if (auto attachment = m_page->attachmentForIdentifier(info.attachmentIdentifier)) {
4008 utiType = attachment->utiType();
4009 fileName = attachment->fileName();
4012 auto provider = adoptNS([[NSFilePromiseProvider alloc] initWithFileType:utiType delegate:(id <NSFilePromiseProviderDelegate>)m_view.getAutoreleased()]);
4013 auto context = adoptNS([[WKPromisedAttachmentContext alloc] initWithIdentifier:info.attachmentIdentifier blobURL:info.blobURL fileName:fileName]);
4014 [provider setUserInfo:context.get()];
4015 auto draggingItem = adoptNS([[NSDraggingItem alloc] initWithPasteboardWriter:provider.get()]);
4016 [draggingItem setDraggingFrame:NSMakeRect(clientDragLocation.x(), clientDragLocation.y() - size.height(), size.width(), size.height()) contents:dragNSImage.get()];
4017 [m_view beginDraggingSessionWithItems:@[draggingItem.get()] event:m_lastMouseDownEvent.get() source:(id <NSDraggingSource>)m_view.getAutoreleased()];
4019 ASSERT(info.additionalTypes.size() == info.additionalData.size());
4020 if (info.additionalTypes.size() == info.additionalData.size()) {
4021 for (size_t index = 0; index < info.additionalTypes.size(); ++index) {
4022 auto nsData = info.additionalData[index]->createNSData();
4023 [pasteboard setData:nsData.get() forType:info.additionalTypes[index]];
4026 m_page->didStartDrag();
4030 [pasteboard setString:@"" forType:PasteboardTypes::WebDummyPboardType];
4031 #pragma clang diagnostic push
4032 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4033 [m_view dragImage:dragNSImage.get() at:NSPointFromCGPoint(clientDragLocation) offset:NSZeroSize event:m_lastMouseDownEvent.get() pasteboard:pasteboard source:m_view.getAutoreleased() slideBack:YES];
4034 #pragma clang diagnostic pop
4035 m_page->didStartDrag();
4038 static bool matchesExtensionOrEquivalent(const String& filename, const String& extension)
4040 return filename.endsWithIgnoringASCIICase("." + extension)
4041 || (equalLettersIgnoringASCIICase(extension, "jpeg") && filename.endsWithIgnoringASCIICase(".jpg"));
4044 void WebViewImpl::setFileAndURLTypes(NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, NSPasteboard *pasteboard)
4046 if (!matchesExtensionOrEquivalent(filename, extension))
4047 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:extension];
4049 [pasteboard setString:visibleURL forType:WebCore::legacyStringPasteboardType()];
4050 [pasteboard setString:visibleURL forType:PasteboardTypes::WebURLPboardType];
4051 [pasteboard setString:title forType:PasteboardTypes::WebURLNamePboardType];
4052 [pasteboard setPropertyList:@[@[visibleURL], @[title]] forType:PasteboardTypes::WebURLsWithTitlesPboardType];
4053 [pasteboard setPropertyList:@[extension] forType:WebCore::legacyFilesPromisePasteboardType()];
4054 m_promisedFilename = filename;
4055 m_promisedURL = url;
4058 void WebViewImpl::setPromisedDataForImage(WebCore::Image* image, NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, WebCore::SharedBuffer* archiveBuffer, NSString *pasteboardName)
4060 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
4061 RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:WebCore::legacyFilesPromisePasteboardType(), nil]);
4063 [types addObjectsFromArray:archiveBuffer ? PasteboardTypes::forImagesWithArchive() : PasteboardTypes::forImages()];
4064 [pasteboard declareTypes:types.get() owner:m_view.getAutoreleased()];
4065 setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
4068 [pasteboard setData:archiveBuffer->createNSData().get() forType:PasteboardTypes::WebArchivePboardType];
4070 m_promisedImage = image;
4073 void WebViewImpl::pasteboardChangedOwner(NSPasteboard *pasteboard)
4075 m_promisedImage = nullptr;
4076 m_promisedFilename = emptyString();
4077 m_promisedURL = emptyString();
4080 void WebViewImpl::provideDataForPasteboard(NSPasteboard *pasteboard, NSString *type)
4082 // FIXME: need to support NSRTFDPboardType
4084 if ([type isEqual:WebCore::legacyTIFFPasteboardType()] && m_promisedImage) {
4085 [pasteboard setData:(__bridge NSData *)m_promisedImage->tiffRepresentation() forType:WebCore::legacyTIFFPasteboardType()];
4086 m_promisedImage = nullptr;
4090 static BOOL fileExists(NSString *path)
4092 struct stat statBuffer;
4093 return !lstat([path fileSystemRepresentation], &statBuffer);
4096 static NSString *pathWithUniqueFilenameForPath(NSString *path)
4098 // "Fix" the filename of the path.
4099 NSString *filename = filenameByFixingIllegalCharacters([path lastPathComponent]);
4100 path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename];
4102 if (fileExists(path)) {
4103 // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename.
4104 NSString *extensions = nil;
4105 NSString *pathWithoutExtensions;
4106 NSString *lastPathComponent = [path lastPathComponent];
4107 NSRange periodRange = [lastPathComponent rangeOfString:@"."];
4109 if (periodRange.location == NSNotFound) {
4110 pathWithoutExtensions = path;
4112 extensions = [lastPathComponent substringFromIndex:periodRange.location + 1];
4113 lastPathComponent = [lastPathComponent substringToIndex:periodRange.location];
4114 pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent];
4117 for (unsigned i = 1; ; i++) {
4118 NSString *pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i];
4119 path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber;
4120 if (!fileExists(path))
4128 NSArray *WebViewImpl::namesOfPromisedFilesDroppedAtDestination(NSURL *dropDestination)
4130 RetainPtr<NSFileWrapper> wrapper;
4131 RetainPtr<NSData> data;