2 * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #import "WebViewImpl.h"
31 #import "APILegacyContextHistoryClient.h"
32 #import "ColorSpaceData.h"
33 #import "GenericCallback.h"
35 #import "NativeWebGestureEvent.h"
36 #import "NativeWebKeyboardEvent.h"
37 #import "NativeWebMouseEvent.h"
38 #import "NativeWebWheelEvent.h"
39 #import "PageClient.h"
40 #import "PageClientImpl.h"
41 #import "PasteboardTypes.h"
42 #import "RemoteLayerTreeDrawingAreaProxy.h"
43 #import "RemoteObjectRegistry.h"
44 #import "RemoteObjectRegistryMessages.h"
45 #import "StringUtilities.h"
46 #import "TextChecker.h"
47 #import "TextCheckerState.h"
48 #import "TiledCoreAnimationDrawingAreaProxy.h"
49 #import "UIGamepadProvider.h"
50 #import "ViewGestureController.h"
51 #import "WKBrowsingContextControllerInternal.h"
52 #import "WKFullScreenWindowController.h"
53 #import "WKImmediateActionController.h"
54 #import "WKPrintingView.h"
55 #import "WKTextInputWindowController.h"
56 #import "WKViewLayoutStrategy.h"
58 #import "WebBackForwardList.h"
59 #import "WebEditCommandProxy.h"
60 #import "WebEventFactory.h"
61 #import "WebInspectorProxy.h"
62 #import "WebPageProxy.h"
63 #import "WebPlaybackSessionManagerProxy.h"
64 #import "WebProcessPool.h"
65 #import "WebProcessProxy.h"
66 #import "_WKRemoteObjectRegistryInternal.h"
67 #import "_WKThumbnailViewInternal.h"
68 #import <HIToolbox/CarbonEventsCore.h>
69 #import <WebCore/AVKitSPI.h>
70 #import <WebCore/AXObjectCache.h>
71 #import <WebCore/ActivityState.h>
72 #import <WebCore/ColorMac.h>
73 #import <WebCore/CoreGraphicsSPI.h>
74 #import <WebCore/DataDetectorsSPI.h>
75 #import <WebCore/DictionaryLookup.h>
76 #import <WebCore/DragData.h>
77 #import <WebCore/Editor.h>
78 #import <WebCore/KeypressCommand.h>
79 #import <WebCore/LocalizedStrings.h>
80 #import <WebCore/LookupSPI.h>
81 #import <WebCore/NSApplicationSPI.h>
82 #import <WebCore/NSImmediateActionGestureRecognizerSPI.h>
83 #import <WebCore/NSSpellCheckerSPI.h>
84 #import <WebCore/NSTextFinderSPI.h>
85 #import <WebCore/NSTouchBarSPI.h>
86 #import <WebCore/NSWindowSPI.h>
87 #import <WebCore/PlatformEventFactoryMac.h>
88 #import <WebCore/SoftLinking.h>
89 #import <WebCore/TextAlternativeWithRange.h>
90 #import <WebCore/TextUndoInsertionMarkupMac.h>
91 #import <WebCore/WebActionDisablingCALayerDelegate.h>
92 #import <WebCore/WebCoreCALayerExtras.h>
93 #import <WebCore/WebCoreFullScreenPlaceholderView.h>
94 #import <WebCore/WebCoreFullScreenWindow.h>
95 #import <WebCore/WebCoreNSStringExtras.h>
96 #import <WebCore/WebPlaybackControlsManager.h>
97 #import <WebKitSystemInterface.h>
99 #import <wtf/NeverDestroyed.h>
100 #import <wtf/SetForScope.h>
102 #if HAVE(TOUCH_BAR) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
103 SOFT_LINK_FRAMEWORK(AVKit)
104 SOFT_LINK_CLASS(AVKit, AVFunctionBarPlaybackControlsProvider)
105 SOFT_LINK_CLASS(AVKit, AVFunctionBarScrubber)
107 static NSString * const WKMediaExitFullScreenItem = @"WKMediaExitFullScreenItem";
108 #endif // HAVE(TOUCH_BAR) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
110 SOFT_LINK_CONSTANT_MAY_FAIL(Lookup, LUNotificationPopoverWillClose, NSString *)
112 @interface NSApplication ()
114 - (void)speakString:(NSString *)string;
115 - (void)stopSpeaking:(id)sender;
118 // FIXME: Move to an SPI header.
119 @interface NSTextInputContext (WKNSTextInputContextDetails)
120 - (void)handleEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler;
121 - (void)handleEventByInputMethod:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler;
122 - (BOOL)handleEventByKeyboardLayout:(NSEvent *)event;
125 @interface WKWindowVisibilityObserver : NSObject {
127 WebKit::WebViewImpl *_impl;
130 - (instancetype)initWithView:(NSView *)view impl:(WebKit::WebViewImpl&)impl;
131 - (void)startObserving:(NSWindow *)window;
132 - (void)stopObserving:(NSWindow *)window;
133 - (void)startObservingFontPanel;
134 - (void)startObservingLookupDismissal;
138 @implementation WKWindowVisibilityObserver
140 - (instancetype)initWithView:(NSView *)view impl:(WebKit::WebViewImpl&)impl
149 NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
150 [workspaceNotificationCenter addObserver:self selector:@selector(_activeSpaceDidChange:) name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
157 if (canLoadLUNotificationPopoverWillClose())
158 [[NSNotificationCenter defaultCenter] removeObserver:self name:getLUNotificationPopoverWillClose() object:nil];
160 NSNotificationCenter *workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
161 [workspaceNotificationCenter removeObserver:self name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
166 static void* keyValueObservingContext = &keyValueObservingContext;
168 - (void)startObserving:(NSWindow *)window
173 NSNotificationCenter *defaultNotificationCenter = [NSNotificationCenter defaultCenter];
175 // An NSView derived object such as WKView cannot observe these notifications, because NSView itself observes them.
176 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidOrderOffScreen:) name:@"NSWindowDidOrderOffScreenNotification" object:window];
177 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidOrderOnScreen:) name:@"_NSWindowDidBecomeVisible" object:window];
179 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
180 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
181 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
182 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
183 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidMove:) name:NSWindowDidMoveNotification object:window];
184 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidResize:) name:NSWindowDidResizeNotification object:window];
185 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeBackingProperties:) name:NSWindowDidChangeBackingPropertiesNotification object:window];
186 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification object:window];
187 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeLayerHosting:) name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
188 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeOcclusionState:) name:NSWindowDidChangeOcclusionStateNotification object:window];
190 [window addObserver:self forKeyPath:@"contentLayoutRect" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
191 [window addObserver:self forKeyPath:@"titlebarAppearsTransparent" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
194 - (void)stopObserving:(NSWindow *)window
199 NSNotificationCenter *defaultNotificationCenter = [NSNotificationCenter defaultCenter];
201 [defaultNotificationCenter removeObserver:self name:@"NSWindowDidOrderOffScreenNotification" object:window];
202 [defaultNotificationCenter removeObserver:self name:@"_NSWindowDidBecomeVisible" object:window];
204 [defaultNotificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
205 [defaultNotificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
206 [defaultNotificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
207 [defaultNotificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
208 [defaultNotificationCenter removeObserver:self name:NSWindowDidMoveNotification object:window];
209 [defaultNotificationCenter removeObserver:self name:NSWindowDidResizeNotification object:window];
210 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window];
211 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeScreenNotification object:window];
212 [defaultNotificationCenter removeObserver:self name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
213 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeOcclusionStateNotification object:window];
215 if (_impl->isEditable())
216 [[NSFontPanel sharedFontPanel] removeObserver:self forKeyPath:@"visible" context:keyValueObservingContext];
217 [window removeObserver:self forKeyPath:@"contentLayoutRect" context:keyValueObservingContext];
218 [window removeObserver:self forKeyPath:@"titlebarAppearsTransparent" context:keyValueObservingContext];
221 - (void)startObservingFontPanel
223 [[NSFontPanel sharedFontPanel] addObserver:self forKeyPath:@"visible" options:0 context:keyValueObservingContext];
226 - (void)startObservingLookupDismissal
228 if (canLoadLUNotificationPopoverWillClose())
229 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_dictionaryLookupPopoverWillClose:) name:getLUNotificationPopoverWillClose() object:nil];
232 - (void)_windowDidOrderOnScreen:(NSNotification *)notification
234 _impl->windowDidOrderOnScreen();
237 - (void)_windowDidOrderOffScreen:(NSNotification *)notification
239 _impl->windowDidOrderOffScreen();
242 - (void)_windowDidBecomeKey:(NSNotification *)notification
244 _impl->windowDidBecomeKey([notification object]);
247 - (void)_windowDidResignKey:(NSNotification *)notification
249 _impl->windowDidResignKey([notification object]);
252 - (void)_windowDidMiniaturize:(NSNotification *)notification
254 _impl->windowDidMiniaturize();
257 - (void)_windowDidDeminiaturize:(NSNotification *)notification
259 _impl->windowDidDeminiaturize();
262 - (void)_windowDidMove:(NSNotification *)notification
264 _impl->windowDidMove();
267 - (void)_windowDidResize:(NSNotification *)notification
269 _impl->windowDidResize();
272 - (void)_windowDidChangeBackingProperties:(NSNotification *)notification
274 CGFloat oldBackingScaleFactor = [[notification.userInfo objectForKey:NSBackingPropertyOldScaleFactorKey] doubleValue];
275 _impl->windowDidChangeBackingProperties(oldBackingScaleFactor);
278 - (void)_windowDidChangeScreen:(NSNotification *)notification
280 _impl->windowDidChangeScreen();
283 - (void)_windowDidChangeLayerHosting:(NSNotification *)notification
285 _impl->windowDidChangeLayerHosting();
288 - (void)_windowDidChangeOcclusionState:(NSNotification *)notification
290 _impl->windowDidChangeOcclusionState();
293 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
295 if (context != keyValueObservingContext) {
296 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
300 if ([keyPath isEqualToString:@"visible"] && [NSFontPanel sharedFontPanelExists] && object == [NSFontPanel sharedFontPanel]) {
301 _impl->updateFontPanelIfNeeded();
304 if ([keyPath isEqualToString:@"contentLayoutRect"] || [keyPath isEqualToString:@"titlebarAppearsTransparent"])
305 _impl->updateContentInsetsIfAutomatic();
308 - (void)_dictionaryLookupPopoverWillClose:(NSNotification *)notification
310 _impl->clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::None);
313 - (void)_activeSpaceDidChange:(NSNotification *)notification
315 _impl->activeSpaceDidChange();
320 @interface WKEditCommandObjC : NSObject {
321 RefPtr<WebKit::WebEditCommandProxy> m_command;
323 - (id)initWithWebEditCommandProxy:(RefPtr<WebKit::WebEditCommandProxy>)command;
324 - (WebKit::WebEditCommandProxy*)command;
327 @interface WKEditorUndoTargetObjC : NSObject
328 - (void)undoEditing:(id)sender;
329 - (void)redoEditing:(id)sender;
332 @implementation WKEditCommandObjC
334 - (id)initWithWebEditCommandProxy:(RefPtr<WebKit::WebEditCommandProxy>)command
344 - (WebKit::WebEditCommandProxy*)command
346 return m_command.get();
351 @implementation WKEditorUndoTargetObjC
353 - (void)undoEditing:(id)sender
355 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
356 [sender command]->unapply();
359 - (void)redoEditing:(id)sender
361 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
362 [sender command]->reapply();
367 @interface WKFlippedView : NSView
370 @implementation WKFlippedView
379 @interface WKResponderChainSink : NSResponder {
380 NSResponder *_lastResponderInChain;
381 bool _didReceiveUnhandledCommand;
384 - (id)initWithResponderChain:(NSResponder *)chain;
386 - (bool)didReceiveUnhandledCommand;
389 @implementation WKResponderChainSink
391 - (id)initWithResponderChain:(NSResponder *)chain
396 _lastResponderInChain = chain;
397 while (NSResponder *next = [_lastResponderInChain nextResponder])
398 _lastResponderInChain = next;
399 [_lastResponderInChain setNextResponder:self];
405 // This assumes that the responder chain was either unmodified since
406 // -initWithResponderChain: was called, or was modified in such a way
407 // that _lastResponderInChain is still in the chain, and self was not
408 // moved earlier in the chain than _lastResponderInChain.
409 NSResponder *responderBeforeSelf = _lastResponderInChain;
410 NSResponder *next = [responderBeforeSelf nextResponder];
411 for (; next && next != self; next = [next nextResponder])
412 responderBeforeSelf = next;
414 // Nothing to be done if we are no longer in the responder chain.
418 [responderBeforeSelf setNextResponder:[self nextResponder]];
419 _lastResponderInChain = nil;
422 - (bool)didReceiveUnhandledCommand
424 return _didReceiveUnhandledCommand;
427 - (void)noResponderFor:(SEL)selector
429 _didReceiveUnhandledCommand = true;
432 - (void)doCommandBySelector:(SEL)selector
434 _didReceiveUnhandledCommand = true;
437 - (BOOL)tryToPerform:(SEL)action with:(id)object
439 _didReceiveUnhandledCommand = true;
445 using namespace WebKit;
449 @interface WKTextListTouchBarViewController : NSViewController {
451 WebViewImpl* _webViewImpl;
452 ListType _currentListType;
455 @property (nonatomic) ListType currentListType;
457 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl;
461 @implementation WKTextListTouchBarViewController
463 @synthesize currentListType=_currentListType;
465 static const CGFloat listControlSegmentWidth = 67;
466 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER) && ENABLE(FULLSCREEN_API)
467 static const CGFloat exitFullScreenButtonWidth = 64;
470 static const NSUInteger noListSegment = 0;
471 static const NSUInteger unorderedListSegment = 1;
472 static const NSUInteger orderedListSegment = 2;
474 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl
476 if (!(self = [super init]))
479 _webViewImpl = webViewImpl;
481 NSSegmentedControl *insertListControl = [NSSegmentedControl segmentedControlWithLabels:@[ WebCore::insertListTypeNone(), WebCore::insertListTypeBulleted(), WebCore::insertListTypeNumbered() ] trackingMode:NSSegmentSwitchTrackingSelectOne target:self action:@selector(_selectList:)];
482 [insertListControl setWidth:listControlSegmentWidth forSegment:noListSegment];
483 [insertListControl setWidth:listControlSegmentWidth forSegment:unorderedListSegment];
484 [insertListControl setWidth:listControlSegmentWidth forSegment:orderedListSegment];
485 insertListControl.font = [NSFont systemFontOfSize:15];
487 #pragma clang diagnostic push
488 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
489 id segmentElement = NSAccessibilityUnignoredDescendant(insertListControl);
490 NSArray *segments = [segmentElement accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
491 ASSERT(segments.count == 3);
492 [segments[noListSegment] accessibilitySetOverrideValue:WebCore::insertListTypeNone() forAttribute:NSAccessibilityDescriptionAttribute];
493 [segments[unorderedListSegment] accessibilitySetOverrideValue:WebCore::insertListTypeBulletedAccessibilityTitle() forAttribute:NSAccessibilityDescriptionAttribute];
494 [segments[orderedListSegment] accessibilitySetOverrideValue:WebCore::insertListTypeNumberedAccessibilityTitle() forAttribute:NSAccessibilityDescriptionAttribute];
495 #pragma clang diagnostic pop
497 self.view = insertListControl;
502 - (void)didDestroyView
504 _webViewImpl = nullptr;
507 - (void)_selectList:(id)sender
512 NSSegmentedControl *insertListControl = (NSSegmentedControl *)self.view;
513 switch (insertListControl.selectedSegment) {
515 // There is no "remove list" edit command, but InsertOrderedList and InsertUnorderedList both
516 // behave as toggles, so we can invoke the appropriate edit command depending on our _currentListType
517 // to remove an existing list. We don't have to do anything if _currentListType is NoList.
518 if (_currentListType == OrderedList)
519 _webViewImpl->page().executeEditCommand(@"InsertOrderedList", @"");
520 else if (_currentListType == UnorderedList)
521 _webViewImpl->page().executeEditCommand(@"InsertUnorderedList", @"");
523 case unorderedListSegment:
524 _webViewImpl->page().executeEditCommand(@"InsertUnorderedList", @"");
526 case orderedListSegment:
527 _webViewImpl->page().executeEditCommand(@"InsertOrderedList", @"");
531 _webViewImpl->dismissTextTouchBarPopoverItemWithIdentifier(NSTouchBarItemIdentifierTextList);
534 - (void)setCurrentListType:(ListType)listType
536 NSSegmentedControl *insertListControl = (NSSegmentedControl *)self.view;
539 [insertListControl setSelected:YES forSegment:noListSegment];
542 [insertListControl setSelected:YES forSegment:orderedListSegment];
545 [insertListControl setSelected:YES forSegment:unorderedListSegment];
549 _currentListType = listType;
554 @interface WKTextTouchBarItemController : NSTextTouchBarItemController <NSCandidateListTouchBarItemDelegate, NSTouchBarDelegate> {
558 BOOL _textIsUnderlined;
559 NSTextAlignment _currentTextAlignment;
560 RetainPtr<NSColor> _textColor;
561 RetainPtr<WKTextListTouchBarViewController> _textListTouchBarViewController;
564 WebViewImpl* _webViewImpl;
567 @property (nonatomic) BOOL textIsBold;
568 @property (nonatomic) BOOL textIsItalic;
569 @property (nonatomic) BOOL textIsUnderlined;
570 @property (nonatomic) NSTextAlignment currentTextAlignment;
571 @property (nonatomic, retain, readwrite) NSColor *textColor;
573 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl;
576 @implementation WKTextTouchBarItemController
578 @synthesize textIsBold=_textIsBold;
579 @synthesize textIsItalic=_textIsItalic;
580 @synthesize textIsUnderlined=_textIsUnderlined;
581 @synthesize currentTextAlignment=_currentTextAlignment;
583 - (instancetype)initWithWebViewImpl:(WebViewImpl*)webViewImpl
585 if (!(self = [super init]))
588 _webViewImpl = webViewImpl;
593 - (void)didDestroyView
595 [[NSNotificationCenter defaultCenter] removeObserver:self];
596 _webViewImpl = nullptr;
597 [_textListTouchBarViewController didDestroyView];
600 #pragma mark NSTouchBarDelegate
602 - (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSString *)identifier
604 return [self itemForIdentifier:identifier];
607 - (nullable NSTouchBarItem *)itemForIdentifier:(NSString *)identifier
609 NSTouchBarItem *item = [super itemForIdentifier:identifier];
610 BOOL isTextFormatItem = [identifier isEqualToString:NSTouchBarItemIdentifierTextFormat];
612 if (isTextFormatItem || [identifier isEqualToString:NSTouchBarItemIdentifierTextStyle])
613 self.textStyle.action = @selector(_wkChangeTextStyle:);
615 if (isTextFormatItem || [identifier isEqualToString:NSTouchBarItemIdentifierTextAlignment])
616 self.textAlignments.action = @selector(_wkChangeTextAlignment:);
618 NSColorPickerTouchBarItem *colorPickerItem = nil;
619 if ([identifier isEqualToString:NSTouchBarItemIdentifierTextColorPicker] && [item isKindOfClass:[NSColorPickerTouchBarItem class]])
620 colorPickerItem = (NSColorPickerTouchBarItem *)item;
621 if (isTextFormatItem)
622 colorPickerItem = self.colorPickerItem;
623 if (colorPickerItem) {
624 colorPickerItem.target = self;
625 colorPickerItem.action = @selector(_wkChangeColor:);
626 colorPickerItem.showsAlpha = NO;
632 #pragma mark NSCandidateListTouchBarItemDelegate
634 - (void)candidateListTouchBarItem:(NSCandidateListTouchBarItem *)anItem endSelectingCandidateAtIndex:(NSInteger)index
636 if (index == NSNotFound)
642 NSArray *candidates = anItem.candidates;
643 if ((NSUInteger)index >= candidates.count)
646 NSTextCheckingResult *candidate = candidates[index];
647 ASSERT([candidate isKindOfClass:[NSTextCheckingResult class]]);
649 _webViewImpl->handleAcceptedCandidate(candidate);
652 - (void)candidateListTouchBarItem:(NSCandidateListTouchBarItem *)anItem changedCandidateListVisibility:(BOOL)isVisible
658 _webViewImpl->requestCandidatesForSelectionIfNeeded();
660 _webViewImpl->updateTouchBar();
663 #pragma mark NSNotificationCenter observers
665 - (void)touchBarDidExitCustomization:(NSNotification *)notification
670 _webViewImpl->setIsCustomizingTouchBar(false);
671 _webViewImpl->updateTouchBar();
674 - (void)touchBarWillEnterCustomization:(NSNotification *)notification
679 _webViewImpl->setIsCustomizingTouchBar(true);
682 - (void)didChangeAutomaticTextCompletion:(NSNotification *)notification
687 _webViewImpl->updateTouchBarAndRefreshTextBarIdentifiers();
691 #pragma mark NSTextTouchBarItemController
693 - (WKTextListTouchBarViewController *)textListTouchBarViewController
695 return (WKTextListTouchBarViewController *)self.textListViewController;
698 - (void)setTextIsBold:(BOOL)bold
701 if ([self.textStyle isSelectedForSegment:0] != _textIsBold)
702 [self.textStyle setSelected:_textIsBold forSegment:0];
705 - (void)setTextIsItalic:(BOOL)italic
707 _textIsItalic = italic;
708 if ([self.textStyle isSelectedForSegment:1] != _textIsItalic)
709 [self.textStyle setSelected:_textIsItalic forSegment:1];
712 - (void)setTextIsUnderlined:(BOOL)underlined
714 _textIsUnderlined = underlined;
715 if ([self.textStyle isSelectedForSegment:2] != _textIsUnderlined)
716 [self.textStyle setSelected:_textIsUnderlined forSegment:2];
719 - (void)_wkChangeTextStyle:(id)sender
724 if ([self.textStyle isSelectedForSegment:0] != _textIsBold) {
725 _textIsBold = !_textIsBold;
726 _webViewImpl->page().executeEditCommand(@"ToggleBold", @"");
729 if ([self.textStyle isSelectedForSegment:1] != _textIsItalic) {
730 _textIsItalic = !_textIsItalic;
731 _webViewImpl->page().executeEditCommand("ToggleItalic", @"");
734 if ([self.textStyle isSelectedForSegment:2] != _textIsUnderlined) {
735 _textIsUnderlined = !_textIsUnderlined;
736 _webViewImpl->page().executeEditCommand("ToggleUnderline", @"");
740 - (void)setCurrentTextAlignment:(NSTextAlignment)alignment
742 _currentTextAlignment = alignment;
743 [self.textAlignments selectSegmentWithTag:_currentTextAlignment];
746 - (void)_wkChangeTextAlignment:(id)sender
751 NSTextAlignment alignment = (NSTextAlignment)[self.textAlignments.cell tagForSegment:self.textAlignments.selectedSegment];
753 case NSTextAlignmentLeft:
754 _currentTextAlignment = NSTextAlignmentLeft;
755 _webViewImpl->page().executeEditCommand("AlignLeft", @"");
757 case NSTextAlignmentRight:
758 _currentTextAlignment = NSTextAlignmentRight;
759 _webViewImpl->page().executeEditCommand("AlignRight", @"");
761 case NSTextAlignmentCenter:
762 _currentTextAlignment = NSTextAlignmentCenter;
763 _webViewImpl->page().executeEditCommand("AlignCenter", @"");
765 case NSTextAlignmentJustified:
766 _currentTextAlignment = NSTextAlignmentJustified;
767 _webViewImpl->page().executeEditCommand("AlignJustified", @"");
773 _webViewImpl->dismissTextTouchBarPopoverItemWithIdentifier(NSTouchBarItemIdentifierTextAlignment);
776 - (NSColor *)textColor
778 return _textColor.get();
781 - (void)setTextColor:(NSColor *)color
784 self.colorPickerItem.color = _textColor.get();
787 - (void)_wkChangeColor:(id)sender
792 _textColor = self.colorPickerItem.color;
793 _webViewImpl->page().executeEditCommand("ForeColor", WebCore::colorFromNSColor(_textColor.get()).serialized());
796 - (NSViewController *)textListViewController
798 if (!_textListTouchBarViewController)
799 _textListTouchBarViewController = adoptNS([[WKTextListTouchBarViewController alloc] initWithWebViewImpl:_webViewImpl]);
800 return _textListTouchBarViewController.get();
807 NSTouchBar *WebViewImpl::makeTouchBar()
809 if (!m_canCreateTouchBars) {
810 m_canCreateTouchBars = true;
813 return m_currentTouchBar.get();
816 void WebViewImpl::updateTouchBar()
818 if (!m_canCreateTouchBars)
821 NSTouchBar *touchBar = nil;
822 bool userActionRequirementsHaveBeenMet = m_requiresUserActionForEditingControlsManager ? m_page->hasHadSelectionChangesFromUserInteraction() : true;
823 if (m_page->editorState().isContentEditable && !m_page->needsHiddenContentEditableQuirk()) {
824 updateTextTouchBar();
825 if (userActionRequirementsHaveBeenMet)
826 touchBar = textTouchBar();
828 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
829 else if (m_page->hasActiveVideoForControlsManager()) {
830 updateMediaTouchBar();
831 // If useMediaPlaybackControlsView() is true, then we are relying on the API client to display a popover version
832 // of the media timeline in their own function bar. If it is false, then we will display the media timeline in our
834 if (!useMediaPlaybackControlsView())
835 touchBar = [m_mediaTouchBarProvider respondsToSelector:@selector(touchBar)] ? [(id)m_mediaTouchBarProvider.get() touchBar] : [(id)m_mediaTouchBarProvider.get() touchBar];
836 } else if ([m_mediaTouchBarProvider playbackControlsController]) {
837 if (m_clientWantsMediaPlaybackControlsView) {
838 if ([m_view respondsToSelector:@selector(_web_didRemoveMediaControlsManager)] && m_view == m_view.window.firstResponder)
839 [m_view _web_didRemoveMediaControlsManager];
841 [m_mediaTouchBarProvider setPlaybackControlsController:nil];
842 [m_mediaPlaybackControlsView setPlaybackControlsController:nil];
846 if (touchBar == m_currentTouchBar)
849 // If m_editableElementIsFocused is true, then we may have a non-editable selection right now just because
850 // the user is clicking or tabbing between editable fields.
851 if (m_editableElementIsFocused && touchBar != textTouchBar())
854 m_currentTouchBar = touchBar;
855 [m_view willChangeValueForKey:@"touchBar"];
856 [m_view setTouchBar:m_currentTouchBar.get()];
857 [m_view didChangeValueForKey:@"touchBar"];
860 NSCandidateListTouchBarItem *WebViewImpl::candidateListTouchBarItem() const
862 return isRichlyEditable() ? m_richTextCandidateListTouchBarItem.get() : m_plainTextCandidateListTouchBarItem.get();
865 AVFunctionBarScrubber *WebViewImpl::mediaPlaybackControlsView() const
867 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
868 if (m_page->hasActiveVideoForControlsManager())
869 return m_mediaPlaybackControlsView.get();
874 bool WebViewImpl::useMediaPlaybackControlsView() const
876 #if ENABLE(FULLSCREEN_API)
877 if (hasFullScreenWindowController())
878 return ![m_fullScreenWindowController isFullScreen];
880 return m_clientWantsMediaPlaybackControlsView;
883 void WebViewImpl::dismissTextTouchBarPopoverItemWithIdentifier(NSString *identifier)
885 NSTouchBarItem *foundItem = nil;
886 for (NSTouchBarItem *item in textTouchBar().items) {
887 if ([item.identifier isEqualToString:identifier]) {
892 if ([item.identifier isEqualToString:NSTouchBarItemIdentifierTextFormat]) {
893 for (NSTouchBarItem *childItem in ((NSGroupTouchBarItem *)item).groupTouchBar.items) {
894 if ([childItem.identifier isEqualToString:identifier]) {
895 foundItem = childItem;
903 if ([foundItem isKindOfClass:[NSPopoverTouchBarItem class]])
904 [(NSPopoverTouchBarItem *)foundItem dismissPopover:nil];
907 static NSArray<NSString *> *textTouchBarCustomizationAllowedIdentifiers()
909 return @[ NSTouchBarItemIdentifierCharacterPicker, NSTouchBarItemIdentifierTextColorPicker, NSTouchBarItemIdentifierTextStyle, NSTouchBarItemIdentifierTextAlignment, NSTouchBarItemIdentifierTextList, NSTouchBarItemIdentifierFlexibleSpace ];
912 static NSArray<NSString *> *plainTextTouchBarDefaultItemIdentifiers()
914 return @[ NSTouchBarItemIdentifierCharacterPicker, NSTouchBarItemIdentifierCandidateList ];
917 static NSArray<NSString *> *richTextTouchBarDefaultItemIdentifiers()
919 return @[ NSTouchBarItemIdentifierCharacterPicker, NSTouchBarItemIdentifierTextFormat, NSTouchBarItemIdentifierCandidateList ];
922 void WebViewImpl::updateTouchBarAndRefreshTextBarIdentifiers()
924 if (m_richTextTouchBar)
925 setUpTextTouchBar(m_richTextTouchBar.get());
927 if (m_plainTextTouchBar)
928 setUpTextTouchBar(m_plainTextTouchBar.get());
933 void WebViewImpl::setUpTextTouchBar(NSTouchBar *touchBar)
935 bool isRichTextTouchBar = touchBar == m_richTextTouchBar.get();
936 [touchBar setDelegate:m_textTouchBarItemController.get()];
937 [touchBar setTemplateItems:[NSMutableSet setWithObject:isRichTextTouchBar ? m_richTextCandidateListTouchBarItem.get() : m_plainTextCandidateListTouchBarItem.get()]];
938 [touchBar setCustomizationAllowedItemIdentifiers:textTouchBarCustomizationAllowedIdentifiers()];
939 [touchBar setDefaultItemIdentifiers:isRichTextTouchBar ? richTextTouchBarDefaultItemIdentifiers() : plainTextTouchBarDefaultItemIdentifiers()];
941 if (NSGroupTouchBarItem *textFormatItem = (NSGroupTouchBarItem *)[touchBar itemForIdentifier:NSTouchBarItemIdentifierTextFormat])
942 textFormatItem.groupTouchBar.customizationIdentifier = @"WKTextFormatTouchBar";
945 bool WebViewImpl::isRichlyEditable() const
947 return m_page->editorState().isContentRichlyEditable && !m_page->needsPlainTextQuirk();
950 NSTouchBar *WebViewImpl::textTouchBar() const
952 return isRichlyEditable() ? m_richTextTouchBar.get() : m_plainTextTouchBar.get();
955 static NSTextAlignment nsTextAlignmentFromTextAlignment(TextAlignment textAlignment)
957 NSTextAlignment nsTextAlignment;
958 switch (textAlignment) {
960 nsTextAlignment = NSTextAlignmentNatural;
963 nsTextAlignment = NSTextAlignmentLeft;
966 nsTextAlignment = NSTextAlignmentRight;
968 case CenterAlignment:
969 nsTextAlignment = NSTextAlignmentCenter;
971 case JustifiedAlignment:
972 nsTextAlignment = NSTextAlignmentJustified;
975 ASSERT_NOT_REACHED();
978 return nsTextAlignment;
981 void WebViewImpl::updateTextTouchBar()
983 if (!m_page->editorState().isContentEditable)
986 if (m_isUpdatingTextTouchBar)
989 SetForScope<bool> isUpdatingTextFunctionBar(m_isUpdatingTextTouchBar, true);
991 if (!m_textTouchBarItemController)
992 m_textTouchBarItemController = adoptNS([[WKTextTouchBarItemController alloc] initWithWebViewImpl:this]);
994 if (!m_startedListeningToCustomizationEvents) {
995 [[NSNotificationCenter defaultCenter] addObserver:m_textTouchBarItemController.get() selector:@selector(touchBarDidExitCustomization:) name:NSTouchBarDidExitCustomization object:nil];
996 [[NSNotificationCenter defaultCenter] addObserver:m_textTouchBarItemController.get() selector:@selector(touchBarWillEnterCustomization:) name:NSTouchBarWillEnterCustomization object:nil];
997 [[NSNotificationCenter defaultCenter] addObserver:m_textTouchBarItemController.get() selector:@selector(didChangeAutomaticTextCompletion:) name:NSSpellCheckerDidChangeAutomaticTextCompletionNotification object:nil];
999 m_startedListeningToCustomizationEvents = true;
1002 if (!m_richTextCandidateListTouchBarItem || !m_plainTextCandidateListTouchBarItem) {
1003 m_richTextCandidateListTouchBarItem = adoptNS([[NSCandidateListTouchBarItem alloc] initWithIdentifier:NSTouchBarItemIdentifierCandidateList]);
1004 [m_richTextCandidateListTouchBarItem setDelegate:m_textTouchBarItemController.get()];
1005 m_plainTextCandidateListTouchBarItem = adoptNS([[NSCandidateListTouchBarItem alloc] initWithIdentifier:NSTouchBarItemIdentifierCandidateList]);
1006 [m_plainTextCandidateListTouchBarItem setDelegate:m_textTouchBarItemController.get()];
1007 requestCandidatesForSelectionIfNeeded();
1010 if (!m_richTextTouchBar) {
1011 m_richTextTouchBar = adoptNS([[NSTouchBar alloc] init]);
1012 setUpTextTouchBar(m_richTextTouchBar.get());
1013 [m_richTextTouchBar setCustomizationIdentifier:@"WKRichTextTouchBar"];
1016 if (!m_plainTextTouchBar) {
1017 m_plainTextTouchBar = adoptNS([[NSTouchBar alloc] init]);
1018 setUpTextTouchBar(m_plainTextTouchBar.get());
1019 [m_plainTextTouchBar setCustomizationIdentifier:@"WKPlainTextTouchBar"];
1022 if ([NSSpellChecker isAutomaticTextCompletionEnabled] && !m_isCustomizingTouchBar) {
1023 BOOL showCandidatesList = !m_page->editorState().selectionIsRange || m_isHandlingAcceptedCandidate;
1024 [candidateListTouchBarItem() updateWithInsertionPointVisibility:showCandidatesList];
1025 [m_view _didUpdateCandidateListVisibility:showCandidatesList];
1028 if (m_page->editorState().isInPasswordField) {
1029 // We don't request candidates for password fields. If the user was previously in a non-password field, then the
1030 // old candidates will still show by default, so we clear them here by setting an empty array of candidates.
1031 if (!m_emptyCandidatesArray)
1032 m_emptyCandidatesArray = adoptNS([[NSArray alloc] init]);
1033 [candidateListTouchBarItem() setCandidates:m_emptyCandidatesArray.get() forSelectedRange:NSMakeRange(0, 0) inString:nil];
1036 NSTouchBar *textTouchBar = this->textTouchBar();
1037 BOOL isShowingCombinedTextFormatItem = [textTouchBar.defaultItemIdentifiers containsObject:NSTouchBarItemIdentifierTextFormat];
1038 [textTouchBar setPrincipalItemIdentifier:isShowingCombinedTextFormatItem ? NSTouchBarItemIdentifierTextFormat : nil];
1040 // Set current typing attributes for rich text. This will ensure that the buttons reflect the state of
1041 // the text when changing selection throughout the document.
1042 if (isRichlyEditable()) {
1043 const EditorState& editorState = m_page->editorState();
1044 if (!editorState.isMissingPostLayoutData) {
1045 [m_textTouchBarItemController setTextIsBold:(bool)(m_page->editorState().postLayoutData().typingAttributes & AttributeBold)];
1046 [m_textTouchBarItemController setTextIsItalic:(bool)(m_page->editorState().postLayoutData().typingAttributes & AttributeItalics)];
1047 [m_textTouchBarItemController setTextIsUnderlined:(bool)(m_page->editorState().postLayoutData().typingAttributes & AttributeUnderline)];
1048 [m_textTouchBarItemController setTextColor:nsColor(editorState.postLayoutData().textColor)];
1049 [[m_textTouchBarItemController textListTouchBarViewController] setCurrentListType:(ListType)m_page->editorState().postLayoutData().enclosingListType];
1050 [m_textTouchBarItemController setCurrentTextAlignment:nsTextAlignmentFromTextAlignment((TextAlignment)editorState.postLayoutData().textAlignment)];
1052 BOOL isShowingCandidateListItem = [textTouchBar.defaultItemIdentifiers containsObject:NSTouchBarItemIdentifierCandidateList] && [NSSpellChecker isAutomaticTextReplacementEnabled];
1053 [m_textTouchBarItemController setUsesNarrowTextStyleItem:isShowingCombinedTextFormatItem && isShowingCandidateListItem];
1057 void WebViewImpl::updateMediaTouchBar()
1059 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
1060 if (!m_mediaTouchBarProvider)
1061 m_mediaTouchBarProvider = adoptNS([allocAVFunctionBarPlaybackControlsProviderInstance() init]);
1063 if (!m_mediaPlaybackControlsView)
1064 m_mediaPlaybackControlsView = adoptNS([allocAVFunctionBarScrubberInstance() init]);
1066 if (!m_playbackControlsManager)
1067 m_playbackControlsManager = adoptNS([[WebPlaybackControlsManager alloc] init]);
1069 if (PlatformWebPlaybackSessionInterface* interface = m_page->playbackSessionManager()->controlsManagerInterface())
1070 [m_playbackControlsManager setWebPlaybackSessionInterfaceMac:interface];
1072 [m_mediaTouchBarProvider setPlaybackControlsController:m_playbackControlsManager.get()];
1073 [m_mediaPlaybackControlsView setPlaybackControlsController:m_playbackControlsManager.get()];
1075 if (!useMediaPlaybackControlsView()) {
1076 #if ENABLE(FULLSCREEN_API)
1077 // If we can't have a media popover function bar item, it might be because we are in full screen.
1078 // If so, customize the escape key.
1079 NSTouchBar *touchBar = [m_mediaTouchBarProvider respondsToSelector:@selector(touchBar)] ? [(id)m_mediaTouchBarProvider.get() touchBar] : [(id)m_mediaTouchBarProvider.get() touchBar];
1080 if (hasFullScreenWindowController() && [m_fullScreenWindowController isFullScreen]) {
1081 if (!m_exitFullScreenButton) {
1082 m_exitFullScreenButton = adoptNS([[NSCustomTouchBarItem alloc] initWithIdentifier:WKMediaExitFullScreenItem]);
1084 NSImage *image = [NSImage imageNamed:NSImageNameTouchBarExitFullScreenTemplate];
1085 [image setTemplate:YES];
1087 NSButton *exitFullScreenButton = [NSButton buttonWithTitle:image ? @"" : @"Exit" image:image target:m_fullScreenWindowController.get() action:@selector(requestExitFullScreen)];
1088 [exitFullScreenButton setAccessibilityTitle:WebCore::exitFullScreenButtonAccessibilityTitle()];
1090 [[exitFullScreenButton.widthAnchor constraintLessThanOrEqualToConstant:exitFullScreenButtonWidth] setActive:YES];
1091 [m_exitFullScreenButton setView:exitFullScreenButton];
1093 touchBar.escapeKeyReplacementItem = m_exitFullScreenButton.get();
1095 touchBar.escapeKeyReplacementItem = nil;
1097 // The rest of the work to update the media function bar only applies to the popover version, so return early.
1101 if (m_playbackControlsManager && m_view == m_view.window.firstResponder && [m_view respondsToSelector:@selector(_web_didAddMediaControlsManager:)])
1102 [m_view _web_didAddMediaControlsManager:m_mediaPlaybackControlsView.get()];
1106 void WebViewImpl::forceRequestCandidatesForTesting()
1108 m_canCreateTouchBars = true;
1114 bool WebViewImpl::shouldRequestCandidates() const
1116 return !m_page->editorState().isInPasswordField && candidateListTouchBarItem().candidateListVisible;
1119 void WebViewImpl::showCandidates(NSArray *candidates, NSString *string, NSRect rectOfTypedString, NSRange selectedRange, NSView *view, void (^completionHandler)(NSTextCheckingResult *acceptedCandidate))
1121 [candidateListTouchBarItem() setCandidates:candidates forSelectedRange:selectedRange inString:string];
1124 void WebViewImpl::setEditableElementIsFocused(bool editableElementIsFocused)
1126 m_editableElementIsFocused = editableElementIsFocused;
1129 // If the editable elements have blurred, then we might need to get rid of the editing function bar.
1130 if (!m_editableElementIsFocused)
1135 } // namespace WebKit
1139 void WebViewImpl::forceRequestCandidatesForTesting()
1143 bool WebViewImpl::shouldRequestCandidates() const
1148 void WebViewImpl::showCandidates(NSArray *candidates, NSString *string, NSRect rectOfTypedString, NSRange selectedRange, NSView *view, void (^completionHandler)(NSTextCheckingResult *acceptedCandidate))
1152 void WebViewImpl::setEditableElementIsFocused(bool editableElementIsFocused)
1154 m_editableElementIsFocused = editableElementIsFocused;
1157 } // namespace WebKit
1158 #endif // HAVE(TOUCH_BAR)
1162 static NSTrackingAreaOptions trackingAreaOptions()
1164 // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
1165 NSTrackingAreaOptions options = NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingInVisibleRect | NSTrackingCursorUpdate;
1166 if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy)
1167 options |= NSTrackingActiveAlways;
1169 options |= NSTrackingActiveInKeyWindow;
1173 WebViewImpl::WebViewImpl(NSView <WebViewImplDelegate> *view, WKWebView *outerWebView, WebProcessPool& processPool, Ref<API::PageConfiguration>&& configuration)
1175 , m_pageClient(std::make_unique<PageClientImpl>(view, outerWebView))
1176 , m_page(processPool.createWebPage(*m_pageClient, WTFMove(configuration)))
1177 , m_weakPtrFactory(this)
1178 , m_needsViewFrameInWindowCoordinates(m_page->preferences().pluginsEnabled())
1179 , m_intrinsicContentSize(CGSizeMake(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric))
1180 , m_layoutStrategy([WKViewLayoutStrategy layoutStrategyWithPage:m_page view:m_view viewImpl:*this mode:kWKLayoutModeViewSize])
1181 , m_undoTarget(adoptNS([[WKEditorUndoTargetObjC alloc] init]))
1182 , m_windowVisibilityObserver(adoptNS([[WKWindowVisibilityObserver alloc] initWithView:view impl:*this]))
1183 , m_primaryTrackingArea(adoptNS([[NSTrackingArea alloc] initWithRect:m_view.frame options:trackingAreaOptions() owner:m_view userInfo:nil]))
1185 static_cast<PageClientImpl&>(*m_pageClient).setImpl(*this);
1187 [NSApp registerServicesMenuSendTypes:PasteboardTypes::forSelection() returnTypes:PasteboardTypes::forEditing()];
1189 [m_view addTrackingArea:m_primaryTrackingArea.get()];
1191 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1193 if (Class gestureClass = NSClassFromString(@"NSImmediateActionGestureRecognizer")) {
1194 m_immediateActionGestureRecognizer = adoptNS([(NSImmediateActionGestureRecognizer *)[gestureClass alloc] init]);
1195 m_immediateActionController = adoptNS([[WKImmediateActionController alloc] initWithPage:m_page view:m_view viewImpl:*this recognizer:m_immediateActionGestureRecognizer.get()]);
1196 [m_immediateActionGestureRecognizer setDelegate:m_immediateActionController.get()];
1197 [m_immediateActionGestureRecognizer setDelaysPrimaryMouseButtonEvents:NO];
1200 m_page->setAddsVisitedLinks(processPool.historyClient().addsVisitedLinks());
1202 m_page->initializeWebPage();
1204 registerDraggedTypes();
1206 m_view.wantsLayer = YES;
1208 // Explicitly set the layer contents placement so AppKit will make sure that our layer has masksToBounds set to YES.
1209 m_view.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
1211 WebProcessPool::statistics().wkViewCount++;
1214 WebViewImpl::~WebViewImpl()
1217 if (m_remoteObjectRegistry) {
1218 m_page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID());
1219 [m_remoteObjectRegistry _invalidate];
1220 m_remoteObjectRegistry = nil;
1224 ASSERT(!m_inSecureInputState);
1227 ASSERT(!m_thumbnailView);
1230 [m_layoutStrategy invalidate];
1232 [m_immediateActionController willDestroyView:m_view];
1235 [m_textTouchBarItemController didDestroyView];
1236 #if ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
1237 [m_mediaTouchBarProvider setPlaybackControlsController:nil];
1238 [m_mediaPlaybackControlsView setPlaybackControlsController:nil];
1244 WebProcessPool::statistics().wkViewCount--;
1248 NSWindow *WebViewImpl::window()
1250 return m_view.window;
1253 void WebViewImpl::processDidExit()
1255 notifyInputContextAboutDiscardedComposition();
1257 if (m_layerHostingView)
1258 setAcceleratedCompositingRootLayer(nil);
1260 updateRemoteAccessibilityRegistration(false);
1262 m_gestureController = nullptr;
1265 void WebViewImpl::pageClosed()
1267 updateRemoteAccessibilityRegistration(false);
1270 void WebViewImpl::didRelaunchProcess()
1272 accessibilityRegisterUIProcessTokens();
1275 void WebViewImpl::setDrawsBackground(bool drawsBackground)
1277 m_page->setDrawsBackground(drawsBackground);
1280 bool WebViewImpl::drawsBackground() const
1282 return m_page->drawsBackground();
1285 bool WebViewImpl::isOpaque() const
1287 return m_page->drawsBackground();
1290 bool WebViewImpl::acceptsFirstResponder()
1295 bool WebViewImpl::becomeFirstResponder()
1297 // If we just became first responder again, there is no need to do anything,
1298 // since resignFirstResponder has correctly detected this situation.
1299 if (m_willBecomeFirstResponderAgain) {
1300 m_willBecomeFirstResponderAgain = false;
1304 NSSelectionDirection direction = [[m_view window] keyViewSelectionDirection];
1306 m_inBecomeFirstResponder = true;
1308 updateSecureInputState();
1309 m_page->activityStateDidChange(WebCore::ActivityState::IsFocused);
1310 // Restore the selection in the editable region if resigning first responder cleared selection.
1311 m_page->restoreSelectionInFocusedEditableElement();
1313 m_inBecomeFirstResponder = false;
1319 if (direction != NSDirectSelection) {
1320 NSEvent *event = [NSApp currentEvent];
1321 NSEvent *keyboardEvent = nil;
1322 if ([event type] == NSEventTypeKeyDown || [event type] == NSEventTypeKeyUp)
1323 keyboardEvent = event;
1324 m_page->setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, false, { }), [](WebKit::CallbackBase::Error) { });
1329 bool WebViewImpl::resignFirstResponder()
1332 // Predict the case where we are losing first responder status only to
1333 // gain it back again. We want resignFirstResponder to do nothing in that case.
1334 id nextResponder = [[m_view window] _newFirstResponderAfterResigning];
1336 // FIXME: This will probably need to change once WKWebView doesn't contain a WKView.
1337 if ([nextResponder isKindOfClass:[WKWebView class]] && m_view.superview == nextResponder) {
1338 m_willBecomeFirstResponderAgain = true;
1343 m_willBecomeFirstResponderAgain = false;
1344 m_inResignFirstResponder = true;
1346 m_page->confirmCompositionAsync();
1348 notifyInputContextAboutDiscardedComposition();
1350 resetSecureInputState();
1352 if (!m_page->maintainsInactiveSelection())
1353 m_page->clearSelection();
1355 m_page->activityStateDidChange(WebCore::ActivityState::IsFocused);
1357 m_inResignFirstResponder = false;
1362 bool WebViewImpl::isFocused() const
1364 if (m_inBecomeFirstResponder)
1366 if (m_inResignFirstResponder)
1368 return m_view.window.firstResponder == m_view;
1371 void WebViewImpl::viewWillStartLiveResize()
1373 m_page->viewWillStartLiveResize();
1375 [m_layoutStrategy willStartLiveResize];
1378 void WebViewImpl::viewDidEndLiveResize()
1380 m_page->viewWillEndLiveResize();
1382 [m_layoutStrategy didEndLiveResize];
1385 void WebViewImpl::renewGState()
1387 if (m_textIndicatorWindow)
1388 dismissContentRelativeChildWindowsWithAnimation(false);
1390 // Update the view frame.
1392 updateWindowAndViewFrames();
1394 updateContentInsetsIfAutomatic();
1397 void WebViewImpl::setFrameSize(CGSize)
1399 [m_layoutStrategy didChangeFrameSize];
1402 void WebViewImpl::disableFrameSizeUpdates()
1404 [m_layoutStrategy disableFrameSizeUpdates];
1407 void WebViewImpl::enableFrameSizeUpdates()
1409 [m_layoutStrategy enableFrameSizeUpdates];
1412 bool WebViewImpl::frameSizeUpdatesDisabled() const
1414 return [m_layoutStrategy frameSizeUpdatesDisabled];
1417 void WebViewImpl::setFrameAndScrollBy(CGRect frame, CGSize offset)
1419 ASSERT(CGSizeEqualToSize(m_resizeScrollOffset, CGSizeZero));
1421 m_resizeScrollOffset = offset;
1422 m_view.frame = NSRectFromCGRect(frame);
1425 void WebViewImpl::updateWindowAndViewFrames()
1427 if (clipsToVisibleRect())
1428 updateViewExposedRect();
1430 if (m_didScheduleWindowAndViewFrameUpdate)
1433 m_didScheduleWindowAndViewFrameUpdate = true;
1435 auto weakThis = createWeakPtr();
1436 dispatch_async(dispatch_get_main_queue(), [weakThis] {
1440 weakThis->m_didScheduleWindowAndViewFrameUpdate = false;
1442 NSRect viewFrameInWindowCoordinates = NSZeroRect;
1443 NSPoint accessibilityPosition = NSZeroPoint;
1445 if (weakThis->m_needsViewFrameInWindowCoordinates)
1446 viewFrameInWindowCoordinates = [weakThis->m_view convertRect:weakThis->m_view.frame toView:nil];
1448 #pragma clang diagnostic push
1449 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1450 if (WebCore::AXObjectCache::accessibilityEnabled())
1451 accessibilityPosition = [[weakThis->m_view accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
1452 #pragma clang diagnostic pop
1454 weakThis->m_page->windowAndViewFramesChanged(viewFrameInWindowCoordinates, accessibilityPosition);
1458 void WebViewImpl::setFixedLayoutSize(CGSize fixedLayoutSize)
1460 m_lastRequestedFixedLayoutSize = fixedLayoutSize;
1462 if (supportsArbitraryLayoutModes())
1463 m_page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
1466 CGSize WebViewImpl::fixedLayoutSize() const
1468 return m_page->fixedLayoutSize();
1471 std::unique_ptr<WebKit::DrawingAreaProxy> WebViewImpl::createDrawingAreaProxy()
1473 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"WebKit2UseRemoteLayerTreeDrawingArea"] boolValue])
1474 return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(m_page);
1476 return std::make_unique<TiledCoreAnimationDrawingAreaProxy>(m_page);
1479 bool WebViewImpl::isUsingUISideCompositing() const
1481 auto* drawingArea = m_page->drawingArea();
1482 return drawingArea && drawingArea->type() == DrawingAreaTypeRemoteLayerTree;
1485 void WebViewImpl::setDrawingAreaSize(CGSize size)
1487 if (!m_page->drawingArea())
1490 m_page->drawingArea()->setSize(WebCore::IntSize(size), WebCore::IntSize(), WebCore::IntSize(m_resizeScrollOffset));
1491 m_resizeScrollOffset = CGSizeZero;
1494 void WebViewImpl::updateLayer()
1496 m_view.layer.backgroundColor = CGColorGetConstantColor(drawsBackground() ? kCGColorWhite : kCGColorClear);
1498 // If asynchronous geometry updates have been sent by forceAsyncDrawingAreaSizeUpdate,
1499 // then subsequent calls to setFrameSize should not result in us waiting for the did
1500 // udpate response if setFrameSize is called.
1501 if (frameSizeUpdatesDisabled())
1504 if (DrawingAreaProxy* drawingArea = m_page->drawingArea())
1505 drawingArea->waitForPossibleGeometryUpdate();
1508 void WebViewImpl::drawRect(CGRect rect)
1510 LOG(Printing, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
1511 m_page->endPrinting();
1514 bool WebViewImpl::canChangeFrameLayout(WebFrameProxy& frame)
1516 // PDF documents are already paginated, so we can't change them to add headers and footers.
1517 return !frame.isDisplayingPDFDocument();
1520 NSPrintOperation *WebViewImpl::printOperationWithPrintInfo(NSPrintInfo *printInfo, WebFrameProxy& frame)
1522 LOG(Printing, "Creating an NSPrintOperation for frame '%s'", frame.url().utf8().data());
1524 // FIXME: If the frame cannot be printed (e.g. if it contains an encrypted PDF that disallows
1525 // printing), this function should return nil.
1526 RetainPtr<WKPrintingView> printingView = adoptNS([[WKPrintingView alloc] initWithFrameProxy:&frame view:m_view]);
1527 // NSPrintOperation takes ownership of the view.
1528 NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get() printInfo:printInfo];
1529 [printOperation setCanSpawnSeparateThread:YES];
1530 [printOperation setJobTitle:frame.title()];
1531 printingView->_printOperation = printOperation;
1532 return printOperation;
1535 void WebViewImpl::setAutomaticallyAdjustsContentInsets(bool automaticallyAdjustsContentInsets)
1537 m_automaticallyAdjustsContentInsets = automaticallyAdjustsContentInsets;
1538 updateContentInsetsIfAutomatic();
1541 void WebViewImpl::updateContentInsetsIfAutomatic()
1543 if (!m_automaticallyAdjustsContentInsets)
1546 NSWindow *window = m_view.window;
1547 if ((window.styleMask & NSWindowStyleMaskFullSizeContentView) && !window.titlebarAppearsTransparent && ![m_view enclosingScrollView]) {
1548 NSRect contentLayoutRect = [m_view convertRect:window.contentLayoutRect fromView:nil];
1549 CGFloat newTopContentInset = NSMaxY(contentLayoutRect) - NSHeight(contentLayoutRect);
1550 if (m_topContentInset != newTopContentInset)
1551 setTopContentInset(newTopContentInset);
1553 setTopContentInset(0);
1556 void WebViewImpl::setTopContentInset(CGFloat contentInset)
1558 m_topContentInset = contentInset;
1560 if (m_didScheduleSetTopContentInset)
1563 m_didScheduleSetTopContentInset = true;
1565 auto weakThis = createWeakPtr();
1566 dispatch_async(dispatch_get_main_queue(), [weakThis] {
1569 weakThis->dispatchSetTopContentInset();
1573 void WebViewImpl::dispatchSetTopContentInset()
1575 if (!m_didScheduleSetTopContentInset)
1578 m_didScheduleSetTopContentInset = false;
1579 m_page->setTopContentInset(m_topContentInset);
1582 void WebViewImpl::prepareContentInRect(CGRect rect)
1584 m_contentPreparationRect = rect;
1585 m_useContentPreparationRectForVisibleRect = true;
1587 updateViewExposedRect();
1590 void WebViewImpl::updateViewExposedRect()
1592 CGRect exposedRect = NSRectToCGRect([m_view visibleRect]);
1594 if (m_useContentPreparationRectForVisibleRect)
1595 exposedRect = CGRectUnion(m_contentPreparationRect, exposedRect);
1597 if (auto drawingArea = m_page->drawingArea())
1598 drawingArea->setViewExposedRect(m_clipsToVisibleRect ? Optional<WebCore::FloatRect>(exposedRect) : Nullopt);
1601 void WebViewImpl::setClipsToVisibleRect(bool clipsToVisibleRect)
1603 m_clipsToVisibleRect = clipsToVisibleRect;
1604 updateViewExposedRect();
1607 void WebViewImpl::setMinimumSizeForAutoLayout(CGSize minimumSizeForAutoLayout)
1609 bool expandsToFit = minimumSizeForAutoLayout.width > 0;
1611 m_page->setMinimumLayoutSize(WebCore::IntSize(minimumSizeForAutoLayout));
1612 m_page->setMainFrameIsScrollable(!expandsToFit);
1614 setClipsToVisibleRect(expandsToFit);
1617 CGSize WebViewImpl::minimumSizeForAutoLayout() const
1619 return m_page->minimumLayoutSize();
1622 void WebViewImpl::setShouldExpandToViewHeightForAutoLayout(bool shouldExpandToViewHeightForAutoLayout)
1624 m_page->setAutoSizingShouldExpandToViewHeight(shouldExpandToViewHeightForAutoLayout);
1627 bool WebViewImpl::shouldExpandToViewHeightForAutoLayout() const
1629 return m_page->autoSizingShouldExpandToViewHeight();
1632 void WebViewImpl::setIntrinsicContentSize(CGSize intrinsicContentSize)
1634 // If the intrinsic content size is less than the minimum layout width, the content flowed to fit,
1635 // so we can report that that dimension is flexible. If not, we need to report our intrinsic width
1636 // so that autolayout will know to provide space for us.
1638 CGSize intrinsicContentSizeAcknowledgingFlexibleWidth = intrinsicContentSize;
1639 if (intrinsicContentSize.width < m_page->minimumLayoutSize().width())
1640 intrinsicContentSizeAcknowledgingFlexibleWidth.width = NSViewNoInstrinsicMetric;
1642 m_intrinsicContentSize = intrinsicContentSizeAcknowledgingFlexibleWidth;
1643 [m_view invalidateIntrinsicContentSize];
1646 CGSize WebViewImpl::intrinsicContentSize() const
1648 return m_intrinsicContentSize;
1651 void WebViewImpl::setViewScale(CGFloat viewScale)
1653 m_lastRequestedViewScale = viewScale;
1655 if (!supportsArbitraryLayoutModes() && viewScale != 1)
1658 if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
1659 [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
1661 m_page->scaleView(viewScale);
1662 [m_layoutStrategy didChangeViewScale];
1665 CGFloat WebViewImpl::viewScale() const
1667 return m_page->viewScaleFactor();
1670 WKLayoutMode WebViewImpl::layoutMode() const
1672 return [m_layoutStrategy layoutMode];
1675 void WebViewImpl::setLayoutMode(WKLayoutMode layoutMode)
1677 m_lastRequestedLayoutMode = layoutMode;
1679 if (!supportsArbitraryLayoutModes() && layoutMode != kWKLayoutModeViewSize)
1682 if (layoutMode == [m_layoutStrategy layoutMode])
1685 [m_layoutStrategy willChangeLayoutStrategy];
1686 m_layoutStrategy = [WKViewLayoutStrategy layoutStrategyWithPage:m_page view:m_view viewImpl:*this mode:layoutMode];
1689 bool WebViewImpl::supportsArbitraryLayoutModes() const
1691 if ([m_fullScreenWindowController isFullScreen])
1694 WebFrameProxy* frame = m_page->mainFrame();
1698 // If we have a plugin document in the main frame, avoid using custom WKLayoutModes
1699 // and fall back to the defaults, because there's a good chance that it won't work (e.g. with PDFPlugin).
1700 if (frame->containsPluginDocument())
1706 void WebViewImpl::updateSupportsArbitraryLayoutModes()
1708 if (!supportsArbitraryLayoutModes()) {
1709 WKLayoutMode oldRequestedLayoutMode = m_lastRequestedLayoutMode;
1710 CGFloat oldRequestedViewScale = m_lastRequestedViewScale;
1711 CGSize oldRequestedFixedLayoutSize = m_lastRequestedFixedLayoutSize;
1713 setLayoutMode(kWKLayoutModeViewSize);
1714 setFixedLayoutSize(CGSizeZero);
1716 // The 'last requested' parameters will have been overwritten by setting them above, but we don't
1717 // want this to count as a request (only changes from the client count), so reset them.
1718 m_lastRequestedLayoutMode = oldRequestedLayoutMode;
1719 m_lastRequestedViewScale = oldRequestedViewScale;
1720 m_lastRequestedFixedLayoutSize = oldRequestedFixedLayoutSize;
1721 } else if (m_lastRequestedLayoutMode != [m_layoutStrategy layoutMode]) {
1722 setViewScale(m_lastRequestedViewScale);
1723 setLayoutMode(m_lastRequestedLayoutMode);
1724 setFixedLayoutSize(m_lastRequestedFixedLayoutSize);
1728 void WebViewImpl::setOverrideDeviceScaleFactor(CGFloat deviceScaleFactor)
1730 m_overrideDeviceScaleFactor = deviceScaleFactor;
1731 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1734 float WebViewImpl::intrinsicDeviceScaleFactor() const
1736 if (m_overrideDeviceScaleFactor)
1737 return m_overrideDeviceScaleFactor;
1738 if (m_targetWindowForMovePreparation)
1739 return m_targetWindowForMovePreparation.backingScaleFactor;
1740 if (NSWindow *window = m_view.window)
1741 return window.backingScaleFactor;
1742 return [NSScreen mainScreen].backingScaleFactor;
1745 void WebViewImpl::windowDidOrderOffScreen()
1747 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible | WebCore::ActivityState::WindowIsActive);
1750 void WebViewImpl::windowDidOrderOnScreen()
1752 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible | WebCore::ActivityState::WindowIsActive);
1755 void WebViewImpl::windowDidBecomeKey(NSWindow *keyWindow)
1757 if (keyWindow == m_view.window || keyWindow == m_view.window.attachedSheet) {
1759 UIGamepadProvider::singleton().viewBecameActive(m_page.get());
1761 updateSecureInputState();
1762 m_page->activityStateDidChange(WebCore::ActivityState::WindowIsActive);
1766 void WebViewImpl::windowDidResignKey(NSWindow *formerKeyWindow)
1768 if (formerKeyWindow == m_view.window || formerKeyWindow == m_view.window.attachedSheet) {
1770 UIGamepadProvider::singleton().viewBecameInactive(m_page.get());
1772 updateSecureInputState();
1773 m_page->activityStateDidChange(WebCore::ActivityState::WindowIsActive);
1777 void WebViewImpl::windowDidMiniaturize()
1779 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1782 void WebViewImpl::windowDidDeminiaturize()
1784 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1787 void WebViewImpl::windowDidMove()
1789 updateWindowAndViewFrames();
1792 void WebViewImpl::windowDidResize()
1794 updateWindowAndViewFrames();
1797 void WebViewImpl::windowDidChangeBackingProperties(CGFloat oldBackingScaleFactor)
1799 CGFloat newBackingScaleFactor = intrinsicDeviceScaleFactor();
1800 if (oldBackingScaleFactor == newBackingScaleFactor)
1803 m_page->setIntrinsicDeviceScaleFactor(newBackingScaleFactor);
1806 void WebViewImpl::windowDidChangeScreen()
1808 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1809 m_page->windowScreenDidChange([[[[window screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue]);
1812 void WebViewImpl::windowDidChangeLayerHosting()
1814 m_page->layerHostingModeDidChange();
1817 void WebViewImpl::windowDidChangeOcclusionState()
1819 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1822 bool WebViewImpl::mightBeginDragWhileInactive()
1824 if (m_view.window.isKeyWindow)
1827 if (m_page->editorState().selectionIsNone || !m_page->editorState().selectionIsRange)
1833 bool WebViewImpl::mightBeginScrollWhileInactive()
1835 // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
1836 if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy)
1842 bool WebViewImpl::acceptsFirstMouse(NSEvent *event)
1844 if (!mightBeginDragWhileInactive() && !mightBeginScrollWhileInactive())
1847 // There's a chance that responding to this event will run a nested event loop, and
1848 // fetching a new event might release the old one. Retaining and then autoreleasing
1849 // the current event prevents that from causing a problem inside WebKit or AppKit code.
1850 [[event retain] autorelease];
1852 if (![m_view hitTest:event.locationInWindow])
1855 setLastMouseDownEvent(event);
1856 bool result = m_page->acceptsFirstMouse(event.eventNumber, WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
1857 setLastMouseDownEvent(nil);
1861 bool WebViewImpl::shouldDelayWindowOrderingForEvent(NSEvent *event)
1863 if (!mightBeginDragWhileInactive())
1866 // There's a chance that responding to this event will run a nested event loop, and
1867 // fetching a new event might release the old one. Retaining and then autoreleasing
1868 // the current event prevents that from causing a problem inside WebKit or AppKit code.
1869 [[event retain] autorelease];
1871 if (![m_view hitTest:event.locationInWindow])
1874 setLastMouseDownEvent(event);
1875 bool result = m_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
1876 setLastMouseDownEvent(nil);
1880 bool WebViewImpl::windowResizeMouseLocationIsInVisibleScrollerThumb(CGPoint point)
1882 NSPoint localPoint = [m_view convertPoint:NSPointFromCGPoint(point) fromView:nil];
1883 NSRect visibleThumbRect = NSRect(m_page->visibleScrollerThumbRect());
1884 return NSMouseInRect(localPoint, visibleThumbRect, m_view.isFlipped);
1887 void WebViewImpl::viewWillMoveToWindow(NSWindow *window)
1889 // If we're in the middle of preparing to move to a window, we should only be moved to that window.
1890 ASSERT(!m_targetWindowForMovePreparation || (m_targetWindowForMovePreparation == window));
1892 NSWindow *currentWindow = m_view.window;
1893 if (window == currentWindow)
1896 clearAllEditCommands();
1898 NSWindow *stopObservingWindow = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1899 [m_windowVisibilityObserver stopObserving:stopObservingWindow];
1900 [m_windowVisibilityObserver startObserving:window];
1903 void WebViewImpl::viewDidMoveToWindow()
1905 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1908 windowDidChangeScreen();
1910 WebCore::ActivityState::Flags activityStateChanges = WebCore::ActivityState::WindowIsActive | WebCore::ActivityState::IsVisible;
1911 if (m_shouldDeferViewInWindowChanges)
1912 m_viewInWindowChangeWasDeferred = true;
1914 activityStateChanges |= WebCore::ActivityState::IsInWindow;
1915 m_page->activityStateDidChange(activityStateChanges);
1917 updateWindowAndViewFrames();
1919 // FIXME(135509) This call becomes unnecessary once 135509 is fixed; remove.
1920 m_page->layerHostingModeDidChange();
1922 if (!m_flagsChangedEventMonitor) {
1923 auto weakThis = createWeakPtr();
1924 m_flagsChangedEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskFlagsChanged handler:[weakThis] (NSEvent *flagsChangedEvent) {
1926 weakThis->postFakeMouseMovedEventForFlagsChangedEvent(flagsChangedEvent);
1927 return flagsChangedEvent;
1931 accessibilityRegisterUIProcessTokens();
1933 if (m_immediateActionGestureRecognizer && ![[m_view gestureRecognizers] containsObject:m_immediateActionGestureRecognizer.get()] && !m_ignoresNonWheelEvents && m_allowsLinkPreview)
1934 [m_view addGestureRecognizer:m_immediateActionGestureRecognizer.get()];
1936 WebCore::ActivityState::Flags activityStateChanges = WebCore::ActivityState::WindowIsActive | WebCore::ActivityState::IsVisible;
1937 if (m_shouldDeferViewInWindowChanges)
1938 m_viewInWindowChangeWasDeferred = true;
1940 activityStateChanges |= WebCore::ActivityState::IsInWindow;
1941 m_page->activityStateDidChange(activityStateChanges);
1943 [NSEvent removeMonitor:m_flagsChangedEventMonitor];
1944 m_flagsChangedEventMonitor = nil;
1946 dismissContentRelativeChildWindowsWithAnimation(false);
1948 if (m_immediateActionGestureRecognizer) {
1949 // Work around <rdar://problem/22646404> by explicitly cancelling the animation.
1950 cancelImmediateActionAnimation();
1951 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
1955 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1958 void WebViewImpl::viewDidChangeBackingProperties()
1960 NSColorSpace *colorSpace = m_view.window.colorSpace;
1961 if ([colorSpace isEqualTo:m_colorSpace.get()])
1964 m_colorSpace = nullptr;
1965 if (DrawingAreaProxy *drawingArea = m_page->drawingArea())
1966 drawingArea->colorSpaceDidChange();
1969 void WebViewImpl::viewDidHide()
1971 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1974 void WebViewImpl::viewDidUnhide()
1976 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1979 void WebViewImpl::activeSpaceDidChange()
1981 m_page->activityStateDidChange(WebCore::ActivityState::IsVisible);
1984 NSView *WebViewImpl::hitTest(CGPoint point)
1986 NSView *hitView = [m_view _web_superHitTest:NSPointFromCGPoint(point)];
1987 if (hitView && hitView == m_layerHostingView)
1993 void WebViewImpl::postFakeMouseMovedEventForFlagsChangedEvent(NSEvent *flagsChangedEvent)
1995 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSEventTypeMouseMoved location:flagsChangedEvent.window.mouseLocationOutsideOfEventStream
1996 modifierFlags:flagsChangedEvent.modifierFlags timestamp:flagsChangedEvent.timestamp windowNumber:flagsChangedEvent.windowNumber
1997 context:nullptr eventNumber:0 clickCount:0 pressure:0];
1998 NativeWebMouseEvent webEvent(fakeEvent, m_lastPressureEvent.get(), m_view);
1999 m_page->handleMouseEvent(webEvent);
2002 ColorSpaceData WebViewImpl::colorSpace()
2004 if (!m_colorSpace) {
2005 if (m_targetWindowForMovePreparation)
2006 m_colorSpace = m_targetWindowForMovePreparation.colorSpace;
2007 else if (NSWindow *window = m_view.window)
2008 m_colorSpace = window.colorSpace;
2010 m_colorSpace = [NSScreen mainScreen].colorSpace;
2013 ColorSpaceData colorSpaceData;
2014 colorSpaceData.cgColorSpace = [m_colorSpace CGColorSpace];
2016 return colorSpaceData;
2019 void WebViewImpl::setUnderlayColor(NSColor *underlayColor)
2021 m_page->setUnderlayColor(WebCore::colorFromNSColor(underlayColor));
2024 NSColor *WebViewImpl::underlayColor() const
2026 WebCore::Color webColor = m_page->underlayColor();
2027 if (!webColor.isValid())
2030 return WebCore::nsColor(webColor);
2033 NSColor *WebViewImpl::pageExtendedBackgroundColor() const
2035 WebCore::Color color = m_page->pageExtendedBackgroundColor();
2036 if (!color.isValid())
2039 return WebCore::nsColor(color);
2042 void WebViewImpl::setOverlayScrollbarStyle(WTF::Optional<WebCore::ScrollbarOverlayStyle> scrollbarStyle)
2044 m_page->setOverlayScrollbarStyle(scrollbarStyle);
2047 WTF::Optional<WebCore::ScrollbarOverlayStyle> WebViewImpl::overlayScrollbarStyle() const
2049 return m_page->overlayScrollbarStyle();
2052 void WebViewImpl::beginDeferringViewInWindowChanges()
2054 if (m_shouldDeferViewInWindowChanges) {
2055 NSLog(@"beginDeferringViewInWindowChanges was called while already deferring view-in-window changes!");
2059 m_shouldDeferViewInWindowChanges = true;
2062 void WebViewImpl::endDeferringViewInWindowChanges()
2064 if (!m_shouldDeferViewInWindowChanges) {
2065 NSLog(@"endDeferringViewInWindowChanges was called without beginDeferringViewInWindowChanges!");
2069 m_shouldDeferViewInWindowChanges = false;
2071 if (m_viewInWindowChangeWasDeferred) {
2072 dispatchSetTopContentInset();
2073 m_page->activityStateDidChange(WebCore::ActivityState::IsInWindow);
2074 m_viewInWindowChangeWasDeferred = false;
2078 void WebViewImpl::endDeferringViewInWindowChangesSync()
2080 if (!m_shouldDeferViewInWindowChanges) {
2081 NSLog(@"endDeferringViewInWindowChangesSync was called without beginDeferringViewInWindowChanges!");
2085 m_shouldDeferViewInWindowChanges = false;
2087 if (m_viewInWindowChangeWasDeferred) {
2088 dispatchSetTopContentInset();
2089 m_page->activityStateDidChange(WebCore::ActivityState::IsInWindow);
2090 m_viewInWindowChangeWasDeferred = false;
2094 void WebViewImpl::prepareForMoveToWindow(NSWindow *targetWindow, std::function<void()> completionHandler)
2096 m_shouldDeferViewInWindowChanges = true;
2097 viewWillMoveToWindow(targetWindow);
2098 m_targetWindowForMovePreparation = targetWindow;
2099 viewDidMoveToWindow();
2101 m_shouldDeferViewInWindowChanges = false;
2103 auto weakThis = createWeakPtr();
2104 m_page->installActivityStateChangeCompletionHandler([weakThis, completionHandler]() {
2105 completionHandler();
2110 ASSERT(weakThis->m_view.window == weakThis->m_targetWindowForMovePreparation);
2111 weakThis->m_targetWindowForMovePreparation = nil;
2114 dispatchSetTopContentInset();
2115 m_page->activityStateDidChange(WebCore::ActivityState::IsInWindow, false, WebPageProxy::ActivityStateChangeDispatchMode::Immediate);
2116 m_viewInWindowChangeWasDeferred = false;
2119 void WebViewImpl::updateSecureInputState()
2121 if (![[m_view window] isKeyWindow] || !isFocused()) {
2122 if (m_inSecureInputState) {
2123 DisableSecureEventInput();
2124 m_inSecureInputState = false;
2128 // WKView has a single input context for all editable areas (except for plug-ins).
2129 NSTextInputContext *context = [m_view _web_superInputContext];
2130 bool isInPasswordField = m_page->editorState().isInPasswordField;
2132 if (isInPasswordField) {
2133 if (!m_inSecureInputState)
2134 EnableSecureEventInput();
2135 static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1];
2136 LOG(TextInput, "-> setAllowedInputSourceLocales:romanInputSources");
2137 [context setAllowedInputSourceLocales:romanInputSources];
2139 if (m_inSecureInputState)
2140 DisableSecureEventInput();
2141 LOG(TextInput, "-> setAllowedInputSourceLocales:nil");
2142 [context setAllowedInputSourceLocales:nil];
2144 m_inSecureInputState = isInPasswordField;
2147 void WebViewImpl::resetSecureInputState()
2149 if (m_inSecureInputState) {
2150 DisableSecureEventInput();
2151 m_inSecureInputState = false;
2155 void WebViewImpl::notifyInputContextAboutDiscardedComposition()
2157 // <rdar://problem/9359055>: -discardMarkedText can only be called for active contexts.
2158 // FIXME: We fail to ever notify the input context if something (e.g. a navigation) happens while the window is not key.
2159 // This is not a problem when the window is key, because we discard marked text on resigning first responder.
2160 if (![[m_view window] isKeyWindow] || m_view != [[m_view window] firstResponder])
2163 LOG(TextInput, "-> discardMarkedText");
2165 [[m_view _web_superInputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to.
2168 void WebViewImpl::setPluginComplexTextInputState(PluginComplexTextInputState pluginComplexTextInputState)
2170 m_pluginComplexTextInputState = pluginComplexTextInputState;
2172 if (m_pluginComplexTextInputState != PluginComplexTextInputDisabled)
2175 // Send back an empty string to the plug-in. This will disable text input.
2176 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, String());
2179 void WebViewImpl::setPluginComplexTextInputStateAndIdentifier(PluginComplexTextInputState pluginComplexTextInputState, uint64_t pluginComplexTextInputIdentifier)
2181 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier) {
2182 // We're asked to update the state for a plug-in that doesn't have focus.
2186 setPluginComplexTextInputState(pluginComplexTextInputState);
2189 void WebViewImpl::disableComplexTextInputIfNecessary()
2191 if (!m_pluginComplexTextInputIdentifier)
2194 if (m_pluginComplexTextInputState != PluginComplexTextInputEnabled)
2197 // Check if the text input window has been dismissed.
2198 if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
2199 setPluginComplexTextInputState(PluginComplexTextInputDisabled);
2202 bool WebViewImpl::handlePluginComplexTextInputKeyDown(NSEvent *event)
2204 ASSERT(m_pluginComplexTextInputIdentifier);
2205 ASSERT(m_pluginComplexTextInputState != PluginComplexTextInputDisabled);
2207 BOOL usingLegacyCocoaTextInput = m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
2209 NSString *string = nil;
2210 BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string];
2213 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, string);
2215 if (!usingLegacyCocoaTextInput)
2216 m_pluginComplexTextInputState = PluginComplexTextInputDisabled;
2219 return didHandleEvent;
2222 bool WebViewImpl::tryHandlePluginComplexTextInputKeyDown(NSEvent *event)
2224 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
2227 // Check if the text input window has been dismissed and let the plug-in process know.
2228 // This is only valid with the updated Cocoa text input spec.
2229 disableComplexTextInputIfNecessary();
2231 // Try feeding the keyboard event directly to the plug-in.
2232 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
2233 return handlePluginComplexTextInputKeyDown(event);
2238 void WebViewImpl::pluginFocusOrWindowFocusChanged(bool pluginHasFocusAndWindowHasFocus, uint64_t pluginComplexTextInputIdentifier)
2240 BOOL inputSourceChanged = m_pluginComplexTextInputIdentifier;
2242 if (pluginHasFocusAndWindowHasFocus) {
2243 // Check if we're already allowing text input for this plug-in.
2244 if (pluginComplexTextInputIdentifier == m_pluginComplexTextInputIdentifier)
2247 m_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier;
2250 // Check if we got a request to unfocus a plug-in that isn't focused.
2251 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier)
2254 m_pluginComplexTextInputIdentifier = 0;
2257 if (inputSourceChanged) {
2258 // The input source changed; discard any entered text.
2259 [[WKTextInputWindowController sharedTextInputWindowController] unmarkText];
2262 // This will force the current input context to be updated to its correct value.
2263 [NSApp updateWindows];
2266 bool WebViewImpl::tryPostProcessPluginComplexTextInputKeyDown(NSEvent *event)
2268 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
2271 // In the legacy text input model, the event has already been sent to the input method.
2272 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
2275 return handlePluginComplexTextInputKeyDown(event);
2278 void WebViewImpl::handleAcceptedAlternativeText(const String& acceptedAlternative)
2280 m_page->handleAlternativeTextUIResult(acceptedAlternative);
2284 NSInteger WebViewImpl::spellCheckerDocumentTag()
2286 if (!m_spellCheckerDocumentTag)
2287 m_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
2288 return m_spellCheckerDocumentTag.value();
2291 void WebViewImpl::pressureChangeWithEvent(NSEvent *event)
2293 #if defined(__LP64__)
2294 if (event == m_lastPressureEvent)
2297 if (m_ignoresNonWheelEvents)
2300 if (event.phase != NSEventPhaseChanged && event.phase != NSEventPhaseBegan && event.phase != NSEventPhaseEnded)
2303 NativeWebMouseEvent webEvent(event, m_lastPressureEvent.get(), m_view);
2304 m_page->handleMouseEvent(webEvent);
2306 m_lastPressureEvent = event;
2310 #if ENABLE(FULLSCREEN_API)
2311 bool WebViewImpl::hasFullScreenWindowController() const
2313 return !!m_fullScreenWindowController;
2316 WKFullScreenWindowController *WebViewImpl::fullScreenWindowController()
2318 if (!m_fullScreenWindowController)
2319 m_fullScreenWindowController = adoptNS([[WKFullScreenWindowController alloc] initWithWindow:createFullScreenWindow() webView:m_view page:m_page]);
2321 return m_fullScreenWindowController.get();
2324 void WebViewImpl::closeFullScreenWindowController()
2326 if (!m_fullScreenWindowController)
2329 [m_fullScreenWindowController close];
2330 m_fullScreenWindowController = nullptr;
2334 NSView *WebViewImpl::fullScreenPlaceholderView()
2336 #if ENABLE(FULLSCREEN_API)
2337 if (m_fullScreenWindowController && [m_fullScreenWindowController isFullScreen])
2338 return [m_fullScreenWindowController webViewPlaceholder];
2343 NSWindow *WebViewImpl::createFullScreenWindow()
2345 #if ENABLE(FULLSCREEN_API)
2346 return [[[WebCoreFullScreenWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] styleMask:(NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable) backing:NSBackingStoreBuffered defer:NO] autorelease];
2352 bool WebViewImpl::isEditable() const
2354 return m_page->isEditable();
2357 typedef HashMap<SEL, String> SelectorNameMap;
2359 // Map selectors into Editor command names.
2360 // This is not needed for any selectors that have the same name as the Editor command.
2361 static const SelectorNameMap& selectorExceptionMap()
2363 static NeverDestroyed<SelectorNameMap> map;
2365 struct SelectorAndCommandName {
2367 ASCIILiteral commandName;
2370 static const SelectorAndCommandName names[] = {
2371 { @selector(insertNewlineIgnoringFieldEditor:), ASCIILiteral("InsertNewline") },
2372 { @selector(insertParagraphSeparator:), ASCIILiteral("InsertNewline") },
2373 { @selector(insertTabIgnoringFieldEditor:), ASCIILiteral("InsertTab") },
2374 { @selector(pageDown:), ASCIILiteral("MovePageDown") },
2375 { @selector(pageDownAndModifySelection:), ASCIILiteral("MovePageDownAndModifySelection") },
2376 { @selector(pageUp:), ASCIILiteral("MovePageUp") },
2377 { @selector(pageUpAndModifySelection:), ASCIILiteral("MovePageUpAndModifySelection") },
2378 { @selector(scrollPageDown:), ASCIILiteral("ScrollPageForward") },
2379 { @selector(scrollPageUp:), ASCIILiteral("ScrollPageBackward") }
2382 for (auto& name : names)
2383 map.get().add(name.selector, name.commandName);
2388 static String commandNameForSelector(SEL selector)
2390 // Check the exception map first.
2391 static const SelectorNameMap& exceptionMap = selectorExceptionMap();
2392 SelectorNameMap::const_iterator it = exceptionMap.find(selector);
2393 if (it != exceptionMap.end())
2396 // Remove the trailing colon.
2397 // No need to capitalize the command name since Editor command names are
2398 // not case sensitive.
2399 const char* selectorName = sel_getName(selector);
2400 size_t selectorNameLength = strlen(selectorName);
2401 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
2403 return String(selectorName, selectorNameLength - 1);
2406 bool WebViewImpl::executeSavedCommandBySelector(SEL selector)
2408 LOG(TextInput, "Executing previously saved command %s", sel_getName(selector));
2409 // The sink does two things: 1) Tells us if the responder went unhandled, and
2410 // 2) prevents any NSBeep; we don't ever want to beep here.
2411 RetainPtr<WKResponderChainSink> sink = adoptNS([[WKResponderChainSink alloc] initWithResponderChain:m_view]);
2412 [m_view _web_superDoCommandBySelector:selector];
2414 return ![sink didReceiveUnhandledCommand];
2417 void WebViewImpl::executeEditCommandForSelector(SEL selector, const String& argument)
2419 m_page->executeEditCommand(commandNameForSelector(selector), argument);
2422 void WebViewImpl::registerEditCommand(RefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo)
2424 RefPtr<WebEditCommandProxy> command = prpCommand;
2426 RetainPtr<WKEditCommandObjC> commandObjC = adoptNS([[WKEditCommandObjC alloc] initWithWebEditCommandProxy:command]);
2427 String actionName = WebEditCommandProxy::nameForEditAction(command->editAction());
2429 NSUndoManager *undoManager = [m_view undoManager];
2430 [undoManager registerUndoWithTarget:m_undoTarget.get() selector:((undoOrRedo == WebPageProxy::Undo) ? @selector(undoEditing:) : @selector(redoEditing:)) object:commandObjC.get()];
2431 if (!actionName.isEmpty())
2432 [undoManager setActionName:(NSString *)actionName];
2435 void WebViewImpl::clearAllEditCommands()
2437 [[m_view undoManager] removeAllActionsWithTarget:m_undoTarget.get()];
2440 bool WebViewImpl::writeSelectionToPasteboard(NSPasteboard *pasteboard, NSArray *types)
2442 size_t numTypes = types.count;
2443 [pasteboard declareTypes:types owner:nil];
2444 for (size_t i = 0; i < numTypes; ++i) {
2445 if ([[types objectAtIndex:i] isEqualTo:NSStringPboardType])
2446 [pasteboard setString:m_page->stringSelectionForPasteboard() forType:NSStringPboardType];
2448 RefPtr<WebCore::SharedBuffer> buffer = m_page->dataSelectionForPasteboard([types objectAtIndex:i]);
2449 [pasteboard setData:buffer ? buffer->createNSData().get() : nil forType:[types objectAtIndex:i]];
2455 bool WebViewImpl::readSelectionFromPasteboard(NSPasteboard *pasteboard)
2457 return m_page->readSelectionFromPasteboard([pasteboard name]);
2460 id WebViewImpl::validRequestorForSendAndReturnTypes(NSString *sendType, NSString *returnType)
2462 EditorState editorState = m_page->editorState();
2463 bool isValidSendType = false;
2465 if (sendType && !editorState.selectionIsNone) {
2466 if (editorState.isInPlugin)
2467 isValidSendType = [sendType isEqualToString:NSStringPboardType];
2469 isValidSendType = [PasteboardTypes::forSelection() containsObject:sendType];
2472 bool isValidReturnType = false;
2474 isValidReturnType = true;
2475 else if ([PasteboardTypes::forEditing() containsObject:returnType] && editorState.isContentEditable) {
2476 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts.
2477 isValidReturnType = editorState.isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType];
2479 if (isValidSendType && isValidReturnType)
2481 return [[m_view nextResponder] validRequestorForSendType:sendType returnType:returnType];
2484 void WebViewImpl::centerSelectionInVisibleArea()
2486 m_page->centerSelectionInVisibleArea();
2489 void WebViewImpl::selectionDidChange()
2491 updateFontPanelIfNeeded();
2492 if (!m_isHandlingAcceptedCandidate)
2493 m_softSpaceRange = NSMakeRange(NSNotFound, 0);
2496 if (!m_page->editorState().isMissingPostLayoutData)
2497 requestCandidatesForSelectionIfNeeded();
2501 void WebViewImpl::startObservingFontPanel()
2503 [m_windowVisibilityObserver startObservingFontPanel];
2506 void WebViewImpl::updateFontPanelIfNeeded()
2508 const EditorState& editorState = m_page->editorState();
2509 if (editorState.selectionIsNone || !editorState.isContentEditable)
2511 if ([NSFontPanel sharedFontPanelExists] && [[NSFontPanel sharedFontPanel] isVisible]) {
2512 m_page->fontAtSelection([](const String& fontName, double fontSize, bool selectionHasMultipleFonts, WebKit::CallbackBase::Error error) {
2513 NSFont *font = [NSFont fontWithName:fontName size:fontSize];
2515 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:selectionHasMultipleFonts];
2520 void WebViewImpl::changeFontFromFontPanel()
2522 NSFontManager *fontManager = [NSFontManager sharedFontManager];
2523 NSFont *font = [fontManager convertFont:fontManager.selectedFont];
2526 m_page->setFont(font.familyName, font.pointSize, font.fontDescriptor.symbolicTraits);
2529 static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item)
2531 if (![(NSObject *)item isKindOfClass:[NSMenuItem class]])
2533 return (NSMenuItem *)item;
2536 static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item)
2538 if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]])
2540 return (NSToolbarItem *)item;
2543 bool WebViewImpl::validateUserInterfaceItem(id <NSValidatedUserInterfaceItem> item)
2545 SEL action = [item action];
2547 if (action == @selector(showGuessPanel:)) {
2548 if (NSMenuItem *menuItem = WebKit::menuItem(item))
2549 [menuItem setTitle:WebCore::contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])];
2550 return m_page->editorState().isContentEditable;
2553 if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:))
2554 return m_page->editorState().isContentEditable;
2556 if (action == @selector(toggleContinuousSpellChecking:)) {
2557 bool enabled = TextChecker::isContinuousSpellCheckingAllowed();
2558 bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled;
2559 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2563 if (action == @selector(toggleGrammarChecking:)) {
2564 bool checked = TextChecker::state().isGrammarCheckingEnabled;
2565 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2569 if (action == @selector(toggleAutomaticSpellingCorrection:)) {
2570 bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled;
2571 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2572 return m_page->editorState().isContentEditable;
2575 if (action == @selector(orderFrontSubstitutionsPanel:)) {
2576 if (NSMenuItem *menuItem = WebKit::menuItem(item))
2577 [menuItem setTitle:WebCore::contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])];
2578 return m_page->editorState().isContentEditable;
2581 if (action == @selector(toggleSmartInsertDelete:)) {
2582 bool checked = m_page->isSmartInsertDeleteEnabled();
2583 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2584 return m_page->editorState().isContentEditable;
2587 if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
2588 bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
2589 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2590 return m_page->editorState().isContentEditable;
2593 if (action == @selector(toggleAutomaticDashSubstitution:)) {
2594 bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled;
2595 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2596 return m_page->editorState().isContentEditable;
2599 if (action == @selector(toggleAutomaticLinkDetection:)) {
2600 bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled;
2601 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2602 return m_page->editorState().isContentEditable;
2605 if (action == @selector(toggleAutomaticTextReplacement:)) {
2606 bool checked = TextChecker::state().isAutomaticTextReplacementEnabled;
2607 [menuItem(item) setState:checked ? NSOnState : NSOffState];
2608 return m_page->editorState().isContentEditable;
2611 if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:))
2612 return m_page->editorState().selectionIsRange && m_page->editorState().isContentEditable;
2614 if (action == @selector(stopSpeaking:))
2615 return [NSApp isSpeaking];
2617 // The centerSelectionInVisibleArea: selector is enabled if there's a selection range or if there's an insertion point in an editable area.
2618 if (action == @selector(centerSelectionInVisibleArea:))
2619 return m_page->editorState().selectionIsRange || (m_page->editorState().isContentEditable && !m_page->editorState().selectionIsNone);
2621 // Next, handle editor commands. Start by returning true for anything that is not an editor command.
2622 // Returning true is the default thing to do in an AppKit validate method for any selector that is not recognized.
2623 String commandName = commandNameForSelector([item action]);
2624 if (!WebCore::Editor::commandIsSupportedFromMenuOrKeyBinding(commandName))
2627 // Add this item to the vector of items for a given command that are awaiting validation.
2628 ValidationMap::AddResult addResult = m_validationMap.add(commandName, ValidationVector());
2629 addResult.iterator->value.append(item);
2630 if (addResult.isNewEntry) {
2631 // If we are not already awaiting validation for this command, start the asynchronous validation process.
2632 // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time
2633 // we asked for the same command; there is no guarantee the answer is still valid.
2634 auto weakThis = createWeakPtr();
2635 m_page->validateCommand(commandName, [weakThis](const String& commandName, bool isEnabled, int32_t state, WebKit::CallbackBase::Error error) {
2639 // If the process exits before the command can be validated, we'll be called back with an error.
2640 if (error != WebKit::CallbackBase::Error::None)
2643 weakThis->setUserInterfaceItemState(commandName, isEnabled, state);
2647 // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called.
2648 // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment.
2649 // But returning NO here would be worse; that would make keyboard commands such as command-C fail.
2653 void WebViewImpl::setUserInterfaceItemState(NSString *commandName, bool enabled, int state)
2655 ValidationVector items = m_validationMap.take(commandName);
2656 for (auto& item : items) {
2657 [menuItem(item.get()) setState:state];
2658 [menuItem(item.get()) setEnabled:enabled];
2659 [toolbarItem(item.get()) setEnabled:enabled];
2660 // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled.
2664 void WebViewImpl::startSpeaking()
2666 m_page->getSelectionOrContentsAsString([](const String& string, WebKit::CallbackBase::Error error) {
2667 if (error != WebKit::CallbackBase::Error::None)
2672 [NSApp speakString:string];
2676 void WebViewImpl::stopSpeaking(id sender)
2678 [NSApp stopSpeaking:sender];
2681 void WebViewImpl::showGuessPanel(id sender)
2683 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2685 LOG_ERROR("No NSSpellChecker");
2689 NSPanel *spellingPanel = [checker spellingPanel];
2690 if ([spellingPanel isVisible]) {
2691 [spellingPanel orderOut:sender];
2695 m_page->advanceToNextMisspelling(true);
2696 [spellingPanel orderFront:sender];
2699 void WebViewImpl::checkSpelling()
2701 m_page->advanceToNextMisspelling(false);
2704 void WebViewImpl::changeSpelling(id sender)
2706 NSString *word = [[sender selectedCell] stringValue];
2708 m_page->changeSpellingToWord(word);
2711 void WebViewImpl::toggleContinuousSpellChecking()
2713 bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled;
2714 TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled);
2716 m_page->process().updateTextCheckerState();
2719 bool WebViewImpl::isGrammarCheckingEnabled()
2721 return TextChecker::state().isGrammarCheckingEnabled;
2724 void WebViewImpl::setGrammarCheckingEnabled(bool flag)
2726 if (static_cast<bool>(flag) == TextChecker::state().isGrammarCheckingEnabled)
2729 TextChecker::setGrammarCheckingEnabled(flag);
2730 m_page->process().updateTextCheckerState();
2733 void WebViewImpl::toggleGrammarChecking()
2735 bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled;
2736 TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled);
2738 m_page->process().updateTextCheckerState();
2741 void WebViewImpl::toggleAutomaticSpellingCorrection()
2743 TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled);
2745 m_page->process().updateTextCheckerState();
2748 void WebViewImpl::orderFrontSubstitutionsPanel(id sender)
2750 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2752 LOG_ERROR("No NSSpellChecker");
2756 NSPanel *substitutionsPanel = [checker substitutionsPanel];
2757 if ([substitutionsPanel isVisible]) {
2758 [substitutionsPanel orderOut:sender];
2761 [substitutionsPanel orderFront:sender];
2764 void WebViewImpl::toggleSmartInsertDelete()
2766 m_page->setSmartInsertDeleteEnabled(!m_page->isSmartInsertDeleteEnabled());
2769 bool WebViewImpl::isAutomaticQuoteSubstitutionEnabled()
2771 return TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
2774 void WebViewImpl::setAutomaticQuoteSubstitutionEnabled(bool flag)
2776 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled)
2779 TextChecker::setAutomaticQuoteSubstitutionEnabled(flag);
2780 m_page->process().updateTextCheckerState();
2783 void WebViewImpl::toggleAutomaticQuoteSubstitution()
2785 TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled);
2786 m_page->process().updateTextCheckerState();
2789 bool WebViewImpl::isAutomaticDashSubstitutionEnabled()
2791 return TextChecker::state().isAutomaticDashSubstitutionEnabled;
2794 void WebViewImpl::setAutomaticDashSubstitutionEnabled(bool flag)
2796 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled)
2799 TextChecker::setAutomaticDashSubstitutionEnabled(flag);
2800 m_page->process().updateTextCheckerState();
2803 void WebViewImpl::toggleAutomaticDashSubstitution()
2805 TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled);
2806 m_page->process().updateTextCheckerState();
2809 bool WebViewImpl::isAutomaticLinkDetectionEnabled()
2811 return TextChecker::state().isAutomaticLinkDetectionEnabled;
2814 void WebViewImpl::setAutomaticLinkDetectionEnabled(bool flag)
2816 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled)
2819 TextChecker::setAutomaticLinkDetectionEnabled(flag);
2820 m_page->process().updateTextCheckerState();
2823 void WebViewImpl::toggleAutomaticLinkDetection()
2825 TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled);
2826 m_page->process().updateTextCheckerState();
2829 bool WebViewImpl::isAutomaticTextReplacementEnabled()
2831 return TextChecker::state().isAutomaticTextReplacementEnabled;
2834 void WebViewImpl::setAutomaticTextReplacementEnabled(bool flag)
2836 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled)
2839 TextChecker::setAutomaticTextReplacementEnabled(flag);
2840 m_page->process().updateTextCheckerState();
2843 void WebViewImpl::toggleAutomaticTextReplacement()
2845 TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled);
2846 m_page->process().updateTextCheckerState();
2849 void WebViewImpl::uppercaseWord()
2851 m_page->uppercaseWord();
2854 void WebViewImpl::lowercaseWord()
2856 m_page->lowercaseWord();
2859 void WebViewImpl::capitalizeWord()
2861 m_page->capitalizeWord();
2864 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
2865 void WebViewImpl::requestCandidatesForSelectionIfNeeded()
2867 if (!shouldRequestCandidates())
2870 const EditorState& editorState = m_page->editorState();
2871 if (!editorState.isContentEditable)
2874 if (editorState.isMissingPostLayoutData)
2877 auto& postLayoutData = editorState.postLayoutData();
2878 m_lastStringForCandidateRequest = postLayoutData.stringForCandidateRequest;
2880 #if HAVE(ADVANCED_SPELL_CHECKING)
2881 NSRange selectedRange = NSMakeRange(postLayoutData.candidateRequestStartPosition, postLayoutData.selectedTextLength);
2882 NSTextCheckingTypes checkingTypes = NSTextCheckingTypeSpelling | NSTextCheckingTypeReplacement | NSTextCheckingTypeCorrection;
2883 auto weakThis = createWeakPtr();
2884 m_lastCandidateRequestSequenceNumber = [[NSSpellChecker sharedSpellChecker] requestCandidatesForSelectedRange:selectedRange inString:postLayoutData.paragraphContextForCandidateRequest types:checkingTypes options:nil inSpellDocumentWithTag:spellCheckerDocumentTag() completionHandler:[weakThis](NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates) {
2885 dispatch_async(dispatch_get_main_queue(), ^{
2888 weakThis->handleRequestedCandidates(sequenceNumber, candidates);
2891 #endif // HAVE(ADVANCED_SPELL_CHECKING)
2894 void WebViewImpl::handleRequestedCandidates(NSInteger sequenceNumber, NSArray<NSTextCheckingResult *> *candidates)
2896 if (!shouldRequestCandidates())
2899 if (m_lastCandidateRequestSequenceNumber != sequenceNumber)
2902 const EditorState& editorState = m_page->editorState();
2903 if (!editorState.isContentEditable)
2906 // FIXME: It's pretty lame that we have to depend on the most recent EditorState having post layout data,
2907 // and that we just bail if it is missing.
2908 if (editorState.isMissingPostLayoutData)
2911 auto& postLayoutData = editorState.postLayoutData();
2912 if (m_lastStringForCandidateRequest != postLayoutData.stringForCandidateRequest)
2915 NSRange selectedRange = NSMakeRange(postLayoutData.candidateRequestStartPosition, postLayoutData.selectedTextLength);
2916 auto weakThis = createWeakPtr();
2917 showCandidates(candidates, postLayoutData.paragraphContextForCandidateRequest, postLayoutData.selectionClipRect, selectedRange, m_view, [weakThis](NSTextCheckingResult *acceptedCandidate) {
2918 dispatch_async(dispatch_get_main_queue(), ^{
2921 weakThis->handleAcceptedCandidate(acceptedCandidate);
2926 static WebCore::TextCheckingResult textCheckingResultFromNSTextCheckingResult(NSTextCheckingResult *nsResult)
2928 WebCore::TextCheckingResult result;
2930 // FIXME: Right now we only request candidates for spelling, replacement, and correction, but we plan to
2931 // support more types, and we will have to update this at that time.
2932 switch ([nsResult resultType]) {
2933 case NSTextCheckingTypeSpelling:
2934 result.type = WebCore::TextCheckingTypeSpelling;
2936 case NSTextCheckingTypeReplacement:
2937 result.type = WebCore::TextCheckingTypeReplacement;
2939 case NSTextCheckingTypeCorrection:
2940 result.type = WebCore::TextCheckingTypeCorrection;
2943 result.type = WebCore::TextCheckingTypeNone;
2946 NSRange resultRange = [nsResult range];
2947 result.location = resultRange.location;
2948 result.length = resultRange.length;
2949 result.replacement = [nsResult replacementString];
2954 void WebViewImpl::handleAcceptedCandidate(NSTextCheckingResult *acceptedCandidate)
2956 const EditorState& editorState = m_page->editorState();
2957 if (!editorState.isContentEditable)
2960 // FIXME: It's pretty lame that we have to depend on the most recent EditorState having post layout data,
2961 // and that we just bail if it is missing.
2962 if (editorState.isMissingPostLayoutData)
2965 auto& postLayoutData = editorState.postLayoutData();
2966 if (m_lastStringForCandidateRequest != postLayoutData.stringForCandidateRequest)
2969 m_isHandlingAcceptedCandidate = true;
2970 NSRange range = [acceptedCandidate range];
2971 if (acceptedCandidate.replacementString && [acceptedCandidate.replacementString length] > 0) {
2972 NSRange replacedRange = NSMakeRange(range.location, [acceptedCandidate.replacementString length]);
2973 NSRange softSpaceRange = NSMakeRange(NSMaxRange(replacedRange) - 1, 1);
2974 if ([acceptedCandidate.replacementString hasSuffix:@" "])
2975 m_softSpaceRange = softSpaceRange;
2978 m_page->handleAcceptedCandidate(textCheckingResultFromNSTextCheckingResult(acceptedCandidate));
2980 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
2982 void WebViewImpl::preferencesDidChange()
2984 BOOL needsViewFrameInWindowCoordinates = m_page->preferences().pluginsEnabled();
2986 if (!!needsViewFrameInWindowCoordinates == !!m_needsViewFrameInWindowCoordinates)
2989 m_needsViewFrameInWindowCoordinates = needsViewFrameInWindowCoordinates;
2991 updateWindowAndViewFrames();
2994 void WebViewImpl::setTextIndicator(WebCore::TextIndicator& textIndicator, WebCore::TextIndicatorWindowLifetime lifetime)
2996 if (!m_textIndicatorWindow)
2997 m_textIndicatorWindow = std::make_unique<WebCore::TextIndicatorWindow>(m_view);
2999 NSRect textBoundingRectInScreenCoordinates = [m_view.window convertRectToScreen:[m_view convertRect:textIndicator.textBoundingRectInRootViewCoordinates() toView:nil]];
3000 m_textIndicatorWindow->setTextIndicator(textIndicator, NSRectToCGRect(textBoundingRectInScreenCoordinates), lifetime);
3003 void WebViewImpl::clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation animation)
3005 if (m_textIndicatorWindow)
3006 m_textIndicatorWindow->clearTextIndicator(animation);
3007 m_textIndicatorWindow = nullptr;
3010 void WebViewImpl::setTextIndicatorAnimationProgress(float progress)
3012 if (m_textIndicatorWindow)
3013 m_textIndicatorWindow->setAnimationProgress(progress);
3016 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimation(bool animate)
3018 [m_view _web_dismissContentRelativeChildWindowsWithAnimation:animate];
3021 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimationFromViewOnly(bool animate)
3023 // Calling _clearTextIndicatorWithAnimation here will win out over the animated clear in dismissContentRelativeChildWindowsFromViewOnly.
3024 // We can't invert these because clients can override (and have overridden) _dismissContentRelativeChildWindows, so it needs to be called.
3025 // For this same reason, this can't be moved to WebViewImpl without care.
3026 clearTextIndicatorWithAnimation(animate ? WebCore::TextIndicatorWindowDismissalAnimation::FadeOut : WebCore::TextIndicatorWindowDismissalAnimation::None);
3027 [m_view _web_dismissContentRelativeChildWindows];
3030 void WebViewImpl::dismissContentRelativeChildWindowsFromViewOnly()
3032 bool hasActiveImmediateAction = false;
3033 hasActiveImmediateAction = [m_immediateActionController hasActiveImmediateAction];
3035 // FIXME: We don't know which panel we are dismissing, it may not even be in the current page (see <rdar://problem/13875766>).
3036 if (m_view.window.isKeyWindow || hasActiveImmediateAction) {
3037 WebCore::DictionaryLookup::hidePopup();
3039 if (DataDetectorsLibrary()) {
3040 DDActionsManager *actionsManager = [getDDActionsManagerClass() sharedManager];
3041 if ([actionsManager respondsToSelector:@selector(requestBubbleClosureUnanchorOnFailure:)])
3042 [actionsManager requestBubbleClosureUnanchorOnFailure:YES];
3046 clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::FadeOut);
3048 [m_immediateActionController dismissContentRelativeChildWindows];
3050 m_pageClient->dismissCorrectionPanel(WebCore::ReasonForDismissingAlternativeTextIgnored);
3053 void WebViewImpl::hideWordDefinitionWindow()
3055 WebCore::DictionaryLookup::hidePopup();
3058 void WebViewImpl::quickLookWithEvent(NSEvent *event)
3060 if (ignoresNonWheelEvents())
3063 if (m_immediateActionGestureRecognizer) {
3064 [m_view _web_superQuickLookWithEvent:event];
3068 NSPoint locationInViewCoordinates = [m_view convertPoint:[event locationInWindow] fromView:nil];
3069 m_page->performDictionaryLookupAtLocation(WebCore::FloatPoint(locationInViewCoordinates));
3072 void WebViewImpl::prepareForDictionaryLookup()
3074 if (m_didRegisterForLookupPopoverCloseNotifications)
3077 m_didRegisterForLookupPopoverCloseNotifications = true;
3079 [m_windowVisibilityObserver startObservingLookupDismissal];
3082 void WebViewImpl::setAllowsLinkPreview(bool allowsLinkPreview)
3084 if (m_allowsLinkPreview == allowsLinkPreview)
3087 m_allowsLinkPreview = allowsLinkPreview;
3089 if (!allowsLinkPreview)
3090 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
3091 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get())
3092 [m_view addGestureRecognizer:immediateActionRecognizer];
3095 void* WebViewImpl::immediateActionAnimationControllerForHitTestResult(API::HitTestResult* hitTestResult, uint32_t type, API::Object* userData)
3097 return [m_view _web_immediateActionAnimationControllerForHitTestResultInternal:hitTestResult withType:type userData:userData];
3100 void WebViewImpl::didPerformImmediateActionHitTest(const WebHitTestResultData& result, bool contentPreventsDefault, API::Object* userData)
3102 [m_immediateActionController didPerformImmediateActionHitTest:result contentPreventsDefault:contentPreventsDefault userData:userData];
3105 void WebViewImpl::prepareForImmediateActionAnimation()
3107 [m_view _web_prepareForImmediateActionAnimation];
3110 void WebViewImpl::cancelImmediateActionAnimation()
3112 [m_view _web_cancelImmediateActionAnimation];
3115 void WebViewImpl::completeImmediateActionAnimation()
3117 [m_view _web_completeImmediateActionAnimation];
3120 void WebViewImpl::didChangeContentSize(CGSize newSize)
3122 [m_view _web_didChangeContentSize:NSSizeFromCGSize(newSize)];
3125 void WebViewImpl::didHandleAcceptedCandidate()
3127 m_isHandlingAcceptedCandidate = false;
3129 [m_view _didHandleAcceptedCandidate];
3132 void WebViewImpl::videoControlsManagerDidChange()
3139 void WebViewImpl::setIgnoresNonWheelEvents(bool ignoresNonWheelEvents)
3141 if (m_ignoresNonWheelEvents == ignoresNonWheelEvents)
3144 m_ignoresNonWheelEvents = ignoresNonWheelEvents;
3145 m_page->setShouldDispatchFakeMouseMoveEvents(!ignoresNonWheelEvents);
3147 if (ignoresNonWheelEvents)
3148 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
3149 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get()) {
3150 if (m_allowsLinkPreview)
3151 [m_view addGestureRecognizer:immediateActionRecognizer];
3155 void WebViewImpl::setIgnoresAllEvents(bool ignoresAllEvents)
3157 m_ignoresAllEvents = ignoresAllEvents;
3158 setIgnoresNonWheelEvents(ignoresAllEvents);
3161 void WebViewImpl::setIgnoresMouseDraggedEvents(bool ignoresMouseDraggedEvents)
3163 m_ignoresMouseDraggedEvents = ignoresMouseDraggedEvents;
3166 void WebViewImpl::setAccessibilityWebProcessToken(NSData *data)
3168 m_remoteAccessibilityChild = WKAXRemoteElementForToken(data);
3169 updateRemoteAccessibilityRegistration(true);
3172 void WebViewImpl::updateRemoteAccessibilityRegistration(bool registerProcess)
3174 // When the tree is connected/disconnected, the remote accessibility registration
3175 // needs to be updated with the pid of the remote process. If the process is going
3176 // away, that information is not present in WebProcess
3178 if (registerProcess)
3179 pid = m_page->process().processIdentifier();
3180 else if (!registerProcess) {
3181 pid = WKAXRemoteProcessIdentifier(m_remoteAccessibilityChild.get());
3182 m_remoteAccessibilityChild = nil;
3185 WKAXRegisterRemoteProcess(registerProcess, pid);
3188 void WebViewImpl::accessibilityRegisterUIProcessTokens()
3190 // Initialize remote accessibility when the window connection has been established.
3191 NSData *remoteElementToken = WKAXRemoteTokenForElement(m_view);
3192 NSData *remoteWindowToken = WKAXRemoteTokenForElement(m_view.window);
3193 IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
3194 IPC::DataReference windowToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]);
3195 m_page->registerUIProcessAccessibilityTokens(elementToken, windowToken);
3198 id WebViewImpl::accessibilityFocusedUIElement()
3200 enableAccessibilityIfNecessary();
3201 return m_remoteAccessibilityChild.get();
3204 id WebViewImpl::accessibilityHitTest(CGPoint)
3206 return accessibilityFocusedUIElement();
3209 void WebViewImpl::enableAccessibilityIfNecessary()
3211 if (WebCore::AXObjectCache::accessibilityEnabled())
3214 // After enabling accessibility update the window frame on the web process so that the
3215 // correct accessibility position is transmitted (when AX is off, that position is not calculated).
3216 WebCore::AXObjectCache::enableAccessibility();
3217 updateWindowAndViewFrames();
3220 id WebViewImpl::accessibilityAttributeValue(NSString *attribute)
3222 enableAccessibilityIfNecessary();
3224 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
3227 if (m_remoteAccessibilityChild)
3228 child = m_remoteAccessibilityChild.get();
3232 return [NSArray arrayWithObject:child];
3234 if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
3235 return NSAccessibilityGroupRole;
3236 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
3237 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
3238 if ([attribute isEqualToString:NSAccessibilityParentAttribute])
3239 return NSAccessibilityUnignoredAncestor([m_view superview]);
3240 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
3243 return [m_view _web_superAccessibilityAttributeValue:attribute];
3246 void WebViewImpl::setPrimaryTrackingArea(NSTrackingArea *trackingArea)
3248 [m_view removeTrackingArea:m_primaryTrackingArea.get()];
3249 m_primaryTrackingArea = trackingArea;
3250 [m_view addTrackingArea:trackingArea];
3253 // Any non-zero value will do, but using something recognizable might help us debug some day.
3254 #define TRACKING_RECT_TAG 0xBADFACE
3256 NSTrackingRectTag WebViewImpl::addTrackingRect(CGRect, id owner, void* userData, bool assumeInside)
3258 ASSERT(m_trackingRectOwner == nil);
3259 m_trackingRectOwner = owner;
3260 m_trackingRectUserData = userData;
3261 return TRACKING_RECT_TAG;
3264 NSTrackingRectTag WebViewImpl::addTrackingRectWithTrackingNum(CGRect, id owner, void* userData, bool assumeInside, int tag)
3266 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
3267 ASSERT(m_trackingRectOwner == nil);
3268 m_trackingRectOwner = owner;
3269 m_trackingRectUserData = userData;
3270 return TRACKING_RECT_TAG;
3273 void WebViewImpl::addTrackingRectsWithTrackingNums(CGRect*, id owner, void** userDataList, bool assumeInside, NSTrackingRectTag *trackingNums, int count)
3276 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
3277 ASSERT(m_trackingRectOwner == nil);
3278 m_trackingRectOwner = owner;
3279 m_trackingRectUserData = userDataList[0];
3280 trackingNums[0] = TRACKING_RECT_TAG;
3283 void WebViewImpl::removeTrackingRect(NSTrackingRectTag tag)
3288 if (tag == TRACKING_RECT_TAG) {
3289 m_trackingRectOwner = nil;
3293 if (tag == m_lastToolTipTag) {
3294 [m_view _web_superRemoveTrackingRect:tag];
3295 m_lastToolTipTag = 0;
3299 // If any other tracking rect is being removed, we don't know how it was created
3300 // and it's possible there's a leak involved (see 3500217)
3301 ASSERT_NOT_REACHED();
3304 void WebViewImpl::removeTrackingRects(NSTrackingRectTag *tags, int count)
3306 for (int i = 0; i < count; ++i) {
3310 ASSERT(tag == TRACKING_RECT_TAG);
3311 m_trackingRectOwner = nil;
3315 void WebViewImpl::sendToolTipMouseExited()
3317 // Nothing matters except window, trackingNumber, and userData.
3318 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSEventTypeMouseExited
3319 location:NSMakePoint(0, 0)
3322 windowNumber:m_view.window.windowNumber
3325 trackingNumber:TRACKING_RECT_TAG
3326 userData:m_trackingRectUserData];
3327 [m_trackingRectOwner mouseExited:fakeEvent];
3330 void WebViewImpl::sendToolTipMouseEntered()
3332 // Nothing matters except window, trackingNumber, and userData.
3333 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSEventTypeMouseEntered
3334 location:NSMakePoint(0, 0)
3337 windowNumber:m_view.window.windowNumber
3340 trackingNumber:TRACKING_RECT_TAG
3341 userData:m_trackingRectUserData];
3342 [m_trackingRectOwner mouseEntered:fakeEvent];
3345 NSString *WebViewImpl::stringForToolTip(NSToolTipTag tag)
3347 return nsStringFromWebCoreString(m_page->toolTip());
3350 void WebViewImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip)
3352 if (!oldToolTip.isNull())
3353 sendToolTipMouseExited();
3355 if (!newToolTip.isEmpty()) {
3356 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
3357 [m_view removeAllToolTips];
3358 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
3359 m_lastToolTipTag = [m_view addToolTipRect:wideOpenRect owner:m_view userData:NULL];
3360 sendToolTipMouseEntered();
3364 void WebViewImpl::setAcceleratedCompositingRootLayer(CALayer *rootLayer)
3366 [rootLayer web_disableAllActions];
3368 m_rootLayer = rootLayer;
3371 if (m_thumbnailView) {
3372 updateThumbnailViewLayer();
3377 [CATransaction begin];
3378 [CATransaction setDisableActions:YES];
3381 if (!m_layerHostingView) {
3382 // Create an NSView that will host our layer tree.
3383 m_layerHostingView = adoptNS([[WKFlippedView alloc] initWithFrame:m_view.bounds]);
3384 [m_layerHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
3386 [m_view addSubview:m_layerHostingView.get() positioned:NSWindowBelow relativeTo:nil];
3388 // Create a root layer that will back the NSView.
3389 RetainPtr<CALayer> layer = adoptNS([[CALayer alloc] init]);
3390 [layer setDelegate:[WebActionDisablingCALayerDelegate shared]];
3392 [layer setName:@"Hosting root layer"];
3395 [m_layerHostingView setLayer:layer.get()];
3396 [m_layerHostingView setWantsLayer:YES];
3399 [m_layerHostingView layer].sublayers = [NSArray arrayWithObject:rootLayer];
3400 } else if (m_layerHostingView) {
3401 [m_layerHostingView removeFromSuperview];
3402 [m_layerHostingView setLayer:nil];
3403 [m_layerHostingView setWantsLayer:NO];
3405 m_layerHostingView = nullptr;
3408 [CATransaction commit];
3412 void WebViewImpl::setThumbnailView(_WKThumbnailView *thumbnailView)
3414 ASSERT(!m_thumbnailView || !thumbnailView);
3416 m_thumbnailView = thumbnailView;
3419 updateThumbnailViewLayer();
3421 setAcceleratedCompositingRootLayer(m_rootLayer.get());
3424 void WebViewImpl::reparentLayerTreeInThumbnailView()
3426 m_thumbnailView._thumbnailLayer = m_rootLayer.get();
3429 void WebViewImpl::updateThumbnailViewLayer()
3431 _WKThumbnailView *thumbnailView = m_thumbnailView;
3432 ASSERT(thumbnailView);
3434 if (thumbnailView._waitingForSnapshot && m_view.window)
3435 reparentLayerTreeInThumbnailView();
3438 void WebViewImpl::setInspectorAttachmentView(NSView *newView)
3440 NSView *oldView = m_inspectorAttachmentView.get();
3441 if (oldView == newView)
3444 m_inspectorAttachmentView = newView;
3445 m_page->inspector()->attachmentViewDidChange(oldView ? oldView : m_view, newView ? newView : m_view);
3448 NSView *WebViewImpl::inspectorAttachmentView()
3450 NSView *attachmentView = m_inspectorAttachmentView.get();
3451 return attachmentView ? attachmentView : m_view;
3454 _WKRemoteObjectRegistry *WebViewImpl::remoteObjectRegistry()
3456 if (!m_remoteObjectRegistry) {
3457 m_remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:m_page]);
3458 m_page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID(), [m_remoteObjectRegistry remoteObjectRegistry]);
3461 return m_remoteObjectRegistry.get();
3464 WKBrowsingContextController *WebViewImpl::browsingContextController()
3466 if (!m_browsingContextController)
3467 m_browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(m_page.ptr())]);
3469 return m_browsingContextController.get();
3471 #endif // WK_API_ENABLED
3473 #if ENABLE(DRAG_SUPPORT)
3474 void WebViewImpl::draggedImage(NSImage *image, CGPoint endPoint, NSDragOperation operation)
3476 #pragma clang diagnostic push
3477 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3478 NSPoint windowImageLoc = [m_view.window convertScreenToBase:NSPointFromCGPoint(endPoint)];
3479 #pragma clang diagnostic pop
3480 NSPoint windowMouseLoc = windowImageLoc;
3482 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
3483 m_ignoresMouseDraggedEvents = true;
3485 m_page->dragEnded(WebCore::IntPoint(windowMouseLoc), WebCore::IntPoint(WebCore::globalPoint(windowMouseLoc, m_view.window)), operation);
3488 static WebCore::DragApplicationFlags applicationFlagsForDrag(NSView *view, id <NSDraggingInfo> draggingInfo)
3491 if ([NSApp modalWindow])
3492 flags = WebCore::DragApplicationIsModal;
3493 if (view.window.attachedSheet)
3494 flags |= WebCore::DragApplicationHasAttachedSheet;
3495 if (draggingInfo.draggingSource == view)
3496 flags |= WebCore::DragApplicationIsSource;
3497 if ([NSApp currentEvent].modifierFlags & NSEventModifierFlagOption)
3498 flags |= WebCore::DragApplicationIsCopyKeyDown;
3499 return static_cast<WebCore::DragApplicationFlags>(flags);
3503 NSDragOperation WebViewImpl::draggingEntered(id <NSDraggingInfo> draggingInfo)
3505 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
3506 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
3507 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
3509 m_page->resetCurrentDragInformation();
3510 m_page->dragEntered(dragData, draggingInfo.draggingPasteboard.name);
3511 return NSDragOperationCopy;
3514 NSDragOperation WebViewImpl::draggingUpdated(id <NSDraggingInfo> draggingInfo)
3516 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
3517 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
3518 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
3519 m_page->dragUpdated(dragData, draggingInfo.draggingPasteboard.name);
3521 NSInteger numberOfValidItemsForDrop = m_page->currentDragNumberOfFilesToBeAccepted();
3522 NSDraggingFormation draggingFormation = NSDraggingFormationNone;
3523 if (m_page->currentDragIsOverFileInput() && numberOfValidItemsForDrop > 0)
3524 draggingFormation = NSDraggingFormationList;
3526 if (draggingInfo.numberOfValidItemsForDrop != numberOfValidItemsForDrop)
3527 [draggingInfo setNumberOfValidItemsForDrop:numberOfValidItemsForDrop];
3528 if (draggingInfo.draggingFormation != draggingFormation)
3529 [draggingInfo setDraggingFormation:draggingFormation];
3531 return m_page->currentDragOperation();
3534 void WebViewImpl::draggingExited(id <NSDraggingInfo> draggingInfo)
3536 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
3537 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
3538 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
3539 m_page->dragExited(dragData, draggingInfo.draggingPasteboard.name);
3540 m_page->resetCurrentDragInformation();
3543 bool WebViewImpl::prepareForDragOperation(id <NSDraggingInfo>)
3548 // FIXME: This code is more or less copied from Pasteboard::getBestURL.
3549 // It would be nice to be able to share the code somehow.
3550 static bool maybeCreateSandboxExtensionFromPasteboard(NSPasteboard *pasteboard, SandboxExtension::Handle& sandboxExtensionHandle)
3552 NSArray *types = pasteboard.types;
3553 if (![types containsObject:NSFilenamesPboardType])
3556 NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
3557 if (files.count != 1)
3560 NSString *file = [files objectAtIndex:0];
3562 if (![[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory])
3568 SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle);
3572 static void createSandboxExtensionsForFileUpload(NSPasteboard *pasteboard, SandboxExtension::HandleArray& handles)
3574 NSArray *types = pasteboard.types;
3575 if (![types containsObject:NSFilenamesPboardType])
3578 NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
3579 handles.allocate(files.count);
3580 for (unsigned i = 0; i < files.count; i++) {
3581 NSString *file = [files objectAtIndex:i];
3582 if (![[NSFileManager defaultManager] fileExistsAtPath:file])
3584 SandboxExtension::Handle handle;
3585 SandboxExtension::createHandle(file, SandboxExtension::ReadOnly, handles[i]);
3589 bool WebViewImpl::performDragOperation(id <NSDraggingInfo> draggingInfo)
3591 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
3592 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
3593 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
3595 SandboxExtension::Handle sandboxExtensionHandle;
3596 bool createdExtension = maybeCreateSandboxExtensionFromPasteboard(draggingInfo.draggingPasteboard, sandboxExtensionHandle);
3597 if (createdExtension)
3598 m_page->process().willAcquireUniversalFileReadSandboxExtension();
3600 SandboxExtension::HandleArray sandboxExtensionForUpload;
3601 createSandboxExtensionsForFileUpload(draggingInfo.draggingPasteboard, sandboxExtensionForUpload);
3603 m_page->performDragOperation(dragData, draggingInfo.draggingPasteboard.name, sandboxExtensionHandle, sandboxExtensionForUpload);
3608 NSView *WebViewImpl::hitTestForDragTypes(CGPoint point, NSSet *types)
3610 // This code is needed to support drag and drop when the drag types cannot be matched.
3611 // This is the case for elements that do not place content
3612 // in the drag pasteboard automatically when the drag start (i.e. dragging a DIV element).
3613 if ([[m_view superview] mouse:NSPointFromCGPoint(point) inRect:[m_view frame]])
3618 void WebViewImpl::registerDraggedTypes()
3620 auto types = adoptNS([[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()]);
3621 [types addObjectsFromArray:PasteboardTypes::forURL()];
3622 [types addObject:PasteboardTypes::WebDummyPboardType];
3623 [m_view registerForDraggedTypes:[types allObjects]];
3625 #endif // ENABLE(DRAG_SUPPORT)
3627 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
3628 void WebViewImpl::startWindowDrag()
3630 [m_view.window performWindowDragWithEvent:m_lastMouseDownEvent.get()];
3634 void WebViewImpl::dragImageForView(NSView *view, NSImage *image, CGPoint clientPoint, bool linkDrag)
3636 // The call below could release the view.
3637 RetainPtr<NSView> protector(m_view);
3638 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
3639 [pasteboard setString:@"" forType:PasteboardTypes::WebDummyPboardType];
3640 #pragma clang diagnostic push
3641 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3642 [view dragImage:image
3643 at:NSPointFromCGPoint(clientPoint)
3645 event:linkDrag ? [NSApp currentEvent] : m_lastMouseDownEvent.get()
3646 pasteboard:pasteboard
3649 #pragma clang diagnostic pop
3652 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
3654 NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
3655 return hasCaseInsensitiveSuffix(filename, extensionAsSuffix) || (stringIsCaseInsensitiveEqualToString(extension, @"jpeg")
3656 && hasCaseInsensitiveSuffix(filename, @".jpg"));
3659 void WebViewImpl::setFileAndURLTypes(NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, NSPasteboard *pasteboard)
3661 if (!matchesExtensionOrEquivalent(filename, extension))
3662 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:extension];
3664 [pasteboard setString:visibleURL forType:NSStringPboardType];
3665 [pasteboard setString:visibleURL forType:PasteboardTypes::WebURLPboardType];
3666 [pasteboard setString:title forType:PasteboardTypes::WebURLNamePboardType];
3667 [pasteboard setPropertyList:[NSArray arrayWithObjects:[NSArray arrayWithObject:visibleURL], [NSArray arrayWithObject:title], nil] forType:PasteboardTypes::WebURLsWithTitlesPboardType];
3668 [pasteboard setPropertyList:[NSArray arrayWithObject:extension] forType:NSFilesPromisePboardType];
3669 m_promisedFilename = filename;
3670 m_promisedURL = url;
3673 void WebViewImpl::setPromisedDataForImage(WebCore::Image* image, NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, WebCore::SharedBuffer* archiveBuffer, NSString *pasteboardName)
3675 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
3676 RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
3678 [types addObjectsFromArray:archiveBuffer ? PasteboardTypes::forImagesWithArchive() : PasteboardTypes::forImages()];
3679 [pasteboard declareTypes:types.get() owner:m_view];
3680 setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
3683 [pasteboard setData:archiveBuffer->createNSData().get() forType:PasteboardTypes::WebArchivePboardType];
3685 m_promisedImage = image;
3688 #if ENABLE(ATTACHMENT_ELEMENT)
3689 void WebViewImpl::setPromisedDataForAttachment(NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, NSString *pasteboardName)
3691 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
3692 RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
3693 [types addObjectsFromArray:PasteboardTypes::forURL()];
3694 [pasteboard declareTypes:types.get() owner:m_view];
3695 setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
3697 RetainPtr<NSMutableArray> paths = adoptNS([[NSMutableArray alloc] init]);
3698 [paths addObject:title];
3699 [pasteboard setPropertyList:paths.get() forType:NSFilenamesPboardType];
3701 m_promisedImage = nullptr;
3705 void WebViewImpl::pasteboardChangedOwner(NSPasteboard *pasteboard)
3707 m_promisedImage = nullptr;
3708 m_promisedFilename = emptyString();
3709 m_promisedURL = emptyString();
3712 void WebViewImpl::provideDataForPasteboard(NSPasteboard *pasteboard, NSString *type)
3714 // FIXME: need to support NSRTFDPboardType
3716 if ([type isEqual:NSTIFFPboardType] && m_promisedImage) {
3717 [pasteboard setData:(NSData *)m_promisedImage->tiffRepresentation() forType:NSTIFFPboardType];
3718 m_promisedImage = nullptr;
3722 static BOOL fileExists(NSString *path)
3724 struct stat statBuffer;
3725 return !lstat([path fileSystemRepresentation], &statBuffer);
3728 static NSString *pathWithUniqueFilenameForPath(NSString *path)
3730 // "Fix" the filename of the path.
3731 NSString *filename = filenameByFixingIllegalCharacters([path lastPathComponent]);
3732 path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename];
3734 if (fileExists(path)) {
3735 // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename.
3736 NSString *extensions = nil;
3737 NSString *pathWithoutExtensions;
3738 NSString *lastPathComponent = [path lastPathComponent];
3739 NSRange periodRange = [lastPathComponent rangeOfString:@"."];
3741 if (periodRange.location == NSNotFound) {
3742 pathWithoutExtensions = path;
3744 extensions = [lastPathComponent substringFromIndex:periodRange.location + 1];
3745 lastPathComponent = [lastPathComponent substringToIndex:periodRange.location];
3746 pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent];
3749 for (unsigned i = 1; ; i++) {
3750 NSString *pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i];
3751 path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber;
3752 if (!fileExists(path))
3760 NSArray *WebViewImpl::namesOfPromisedFilesDroppedAtDestination(NSURL *dropDestination)
3762 RetainPtr<NSFileWrapper> wrapper;
3763 RetainPtr<NSData> data;
3765 if (m_promisedImage) {
3766 data = m_promisedImage->data()->createNSData();
3767 wrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data.get()]);
3769 wrapper = adoptNS([[NSFileWrapper alloc] initWithURL:[NSURL URLWithString:m_promisedURL] options:NSFileWrapperReadingImmediate error:nil]);
3772 [wrapper setPreferredFilename:m_promisedFilename];
3774 LOG_ERROR("Failed to create image file.");
3778 // FIXME: Report an error if we fail to create a file.
3779 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
3780 path = pathWithUniqueFilenameForPath(path);
3781 if (![wrapper writeToURL:[NSURL fileURLWithPath:path] options:NSFileWrapperWritingWithNameUpdating originalContentsURL:nil error:nullptr])
3782 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToURL:options:originalContentsURL:error:]");
3784 if (!m_promisedURL.isEmpty())
3785 WebCore::setMetadataURL(m_promisedURL, "", String(path));
3787 return [NSArray arrayWithObject:[path lastPathComponent]];
3790 static RetainPtr<CGImageRef> takeWindowSnapshot(CGSWindowID windowID, bool captureAtNominalResolution)
3792 CGSWindowCaptureOptions options = kCGSCaptureIgnoreGlobalClipShape;
3793 if (captureAtNominalResolution)
3794 options |= kCGSWindowCaptureNominalResolution;
3795 RetainPtr<CFArrayRef> windowSnapshotImages = adoptCF(CGSHWCaptureWindowList(CGSMainConnectionID(), &windowID, 1, options));
3797 if (windowSnapshotImages && CFArrayGetCount(windowSnapshotImages.get()))
3798 return (CGImageRef)CFArrayGetValueAtIndex(windowSnapshotImages.get(), 0);
3800 // Fall back to the non-hardware capture path if we didn't get a snapshot
3801 // (which usually happens if the window is fully off-screen).
3802 CGWindowImageOption imageOptions = kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque;
3803 if (captureAtNominalResolution)
3804 imageOptions |= kCGWindowImageNominalResolution;
3805 return adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, imageOptions));
3808 RefPtr<ViewSnapshot> WebViewImpl::takeViewSnapshot()
3810 NSWindow *window = m_view.window;
3812 CGSWindowID windowID = (CGSWindowID)window.windowNumber;
3813 if (!windowID || !window.isVisible)
3816 RetainPtr<CGImageRef> windowSnapshotImage = takeWindowSnapshot(windowID, false);
3817 if (!windowSnapshotImage)
3820 // Work around <rdar://problem/17084993>; re-request the snapshot at kCGWindowImageNominalResolution if it was captured at the wrong scale.
3821 CGFloat desiredSnapshotWidth = window.frame.size.width * window.screen.backingScaleFactor;
3822 if (CGImageGetWidth(windowSnapshotImage.get()) != desiredSnapshotWidth)
3823 windowSnapshotImage = takeWindowSnapshot(windowID, true);
3825 if (!windowSnapshotImage)
3828 ViewGestureController& gestureController = ensureGestureController();
3830 NSRect windowCaptureRect;
3831 WebCore::FloatRect boundsForCustomSwipeViews = gestureController.windowRelativeBoundsForCustomSwipeViews();
3832 if (!boundsForCustomSwipeViews.isEmpty())
3833 windowCaptureRect = boundsForCustomSwipeViews;
3835 NSRect unobscuredBounds = m_view.bounds;
3836 float topContentInset = m_page->topContentInset();
3837 unobscuredBounds.origin.y += topContentInset;
3838 unobscuredBounds.size.height -= topContentInset;
3839 windowCaptureRect = [m_view convertRect:unobscuredBounds toView:nil];
3842 NSRect windowCaptureScreenRect = [window convertRectToScreen:windowCaptureRect];
3843 CGRect windowScreenRect;
3844 CGSGetScreenRectForWindow(CGSMainConnectionID(), (CGSWindowID)[window windowNumber], &windowScreenRect);
3846 NSRect croppedImageRect = windowCaptureRect;
3847 croppedImageRect.origin.y = windowScreenRect.size.height - windowCaptureScreenRect.size.height - NSMinY(windowCaptureRect);
3849 auto croppedSnapshotImage = adoptCF(CGImageCreateWithImageInRect(windowSnapshotImage.get(), NSRectToCGRect([window convertRectToBacking:croppedImageRect])));
3851 auto surface = WebCore::IOSurface::createFromImage(croppedSnapshotImage.get());
3855 RefPtr<ViewSnapshot> snapshot = ViewSnapshot::create(WTFMove(surface));
3856 snapshot->setVolatile(true);
3861 void WebViewImpl::saveBackForwardSnapshotForCurrentItem()
3863 if (WebBackForwardListItem* item = m_page->backForwardList().currentItem())
3864 m_page->recordNavigationSnapshot(*item);
3867 void WebViewImpl::saveBackForwardSnapshotForItem(WebBackForwardListItem& item)
3869 m_page->recordNavigationSnapshot(item);
3872 ViewGestureController& WebViewImpl::ensureGestureController()
3874 if (!m_gestureController)
3875 m_gestureController = std::make_unique<ViewGestureController>(m_page);
3876 return *m_gestureController;
3879 void WebViewImpl::setAllowsBackForwardNavigationGestures(bool allowsBackForwardNavigationGestures)
3881 m_allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
3882 m_page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
3883 m_page->setShouldUseImplicitRubberBandControl(allowsBackForwardNavigationGestures);
3886 void WebViewImpl::setAllowsMagnification(bool allowsMagnification)
3888 m_allowsMagnification = allowsMagnification;
3891 void WebViewImpl::setMagnification(double magnification, CGPoint centerPoint)
3893 if (magnification <= 0 || isnan(magnification) || isinf(magnification))
3894 [NSException raise:NSInvalidArgumentException format:@"Magnification should be a positive number"];
3896 dismissContentRelativeChildWindowsWithAnimation(false);
3898 m_page->scalePageInViewCoordinates(magnification, WebCore::roundedIntPoint(centerPoint));
3901 void WebViewImpl::setMagnification(double magnification)
3903 if (magnification <= 0 || isnan(magnification) || isinf(magnification))
3904 [NSException raise:NSInvalidArgumentException format:@"Magnification should be a positive number"];
3906 dismissContentRelativeChildWindowsWithAnimation(false);
3908 WebCore::FloatPoint viewCenter(NSMidX([m_view bounds]), NSMidY([m_view bounds]));
3909 m_page->scalePageInViewCoordinates(magnification, roundedIntPoint(viewCenter));
3912 double WebViewImpl::magnification() const
3914 if (m_gestureController)
3915 return m_gestureController->magnification();
3916 return m_page->pageScaleFactor();
3919 void WebViewImpl::setCustomSwipeViews(NSArray *customSwipeViews)
3921 if (!customSwipeViews.count && !m_gestureController)
3924 Vector<RetainPtr<NSView>> views;
3925 views.reserveInitialCapacity(customSwipeViews.count);
3926 for (NSView *view in customSwipeViews)
3927 views.uncheckedAppend(view);
3929 ensureGestureController().setCustomSwipeViews(views);
3932 void WebViewImpl::setCustomSwipeViewsTopContentInset(float topContentInset)
3934 ensureGestureController().setCustomSwipeViewsTopContentInset(topContentInset);
3937 bool WebViewImpl::tryToSwipeWithEvent(NSEvent *event, bool ignoringPinnedState)
3939 if (!m_allowsBackForwardNavigationGestures)
3942 auto& gestureController = ensureGestureController();
3944 bool wasIgnoringPinnedState = gestureController.shouldIgnorePinnedState();
3945 gestureController.setShouldIgnorePinnedState(ignoringPinnedState);
3947 bool handledEvent = gestureController.handleScrollWheelEvent(event);
3949 gestureController.setShouldIgnorePinnedState(wasIgnoringPinnedState);
3951 return handledEvent;
3954 void WebViewImpl::setDidMoveSwipeSnapshotCallback(BlockPtr<void (CGRect)>&& callback)
3956 if (!m_allowsBackForwardNavigationGestures)
3959 ensureGestureController().setDidMoveSwipeSnapshotCallback(WTFMove(callback));
3962 void WebViewImpl::scrollWheel(NSEvent *event)
3964 if (m_ignoresAllEvents)
3967 if (event.phase == NSEventPhaseBegan)
3968 dismissContentRelativeChildWindowsWithAnimation(false);
3970 if (m_allowsBackForwardNavigationGestures && ensureGestureController().handleScrollWheelEvent(event))
3973 NativeWebWheelEvent webEvent = NativeWebWheelEvent(event, m_view);
3974 m_page->handleWheelEvent(webEvent);
3977 void WebViewImpl::swipeWithEvent(NSEvent *event)
3979 if (m_ignoresNonWheelEvents)
3982 if (!m_allowsBackForwardNavigationGestures) {
3983 [m_view _web_superSwipeWithEvent:event];
3987 if (event.deltaX > 0.0)
3989 else if (event.deltaX < 0.0)
3990 m_page->goForward();
3992 [m_view _web_superSwipeWithEvent:event];
3995 void WebViewImpl::magnifyWithEvent(NSEvent *event)
3997 if (!m_allowsMagnification) {
3998 #if ENABLE(MAC_GESTURE_EVENTS)
3999 NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
4000 m_page->handleGestureEvent(webEvent);
4002 [m_view _web_superMagnifyWithEvent:event];
4006 dismissContentRelativeChildWindowsWithAnimation(false);
4008 auto& gestureController = ensureGestureController();
4010 #if ENABLE(MAC_GESTURE_EVENTS)
4011 if (gestureController.hasActiveMagnificationGesture()) {
4012 gestureController.handleMagnificationGestureEvent(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
4016 NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
4017 m_page->handleGestureEvent(webEvent);
4019 gestureController.handleMagnificationGestureEvent(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
4023 void WebViewImpl::smartMagnifyWithEvent(NSEvent *event)
4025 if (!m_allowsMagnification) {
4026 [m_view _web_superSmartMagnifyWithEvent:event];
4030 dismissContentRelativeChildWindowsWithAnimation(false);
4032 ensureGestureController().handleSmartMagnificationGesture([m_view convertPoint:event.locationInWindow fromView:nil]);
4035 void WebViewImpl::setLastMouseDownEvent(NSEvent *event)
4037 ASSERT(!event || event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown);
4039 if (event == m_lastMouseDownEvent.get())
4042 m_lastMouseDownEvent = event;
4045 #if ENABLE(MAC_GESTURE_EVENTS)
4046 void WebViewImpl::rotateWithEvent(NSEvent *event)
4048 NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
4049 m_page->handleGestureEvent(webEvent);
4053 void WebViewImpl::gestureEventWasNotHandledByWebCore(NSEvent *event)
4055 [m_view _web_gestureEventWasNotHandledByWebCore:event];
4058 void WebViewImpl::gestureEventWasNotHandledByWebCoreFromViewOnly(NSEvent *event)
4060 #if ENABLE(MAC_GESTURE_EVENTS)
4061 if (m_allowsMagnification && m_gestureController)
4062 m_gestureController->gestureEventWasNotHandledByWebCore(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
4066 void WebViewImpl::didRestoreScrollPosition()
4068 if (m_gestureController)
4069 m_gestureController->didRestoreScrollPosition();
4072 void WebViewImpl::doneWithKeyEvent(NSEvent *event, bool eventWasHandled)
4074 if ([event type] != NSEventTypeKeyDown)
4077 if (tryPostProcessPluginComplexTextInputKeyDown(event))
4080 if (eventWasHandled) {
4081 [NSCursor setHiddenUntilMouseMoves:YES];
4085 // resending the event may destroy this WKView
4086 RetainPtr<NSView> protector(m_view);
4088 ASSERT(!m_keyDownEventBeingResent);
4089 m_keyDownEventBeingResent = event;
4090 [NSApp _setCurrentEvent:event];
4091 [NSApp sendEvent:event];
4093 m_keyDownEventBeingResent = nullptr;
4096 NSArray *WebViewImpl::validAttributesForMarkedText()
4098 static NSArray *validAttributes;
4099 if (!validAttributes) {
4100 validAttributes = @[ NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, NSMarkedClauseSegmentAttributeName,
4101 #if USE(DICTATION_ALTERNATIVES)
4102 NSTextAlternativesAttributeName,
4104 #if USE(INSERTION_UNDO_GROUPING)
4105 NSTextInsertionUndoableAttributeName,
4108 // NSText also supports the following attributes, but it's
4109 // hard to tell which are really required for text input to
4110 // work well; I have not seen any input method make use of them yet.
4111 // NSFontAttributeName, NSForegroundColorAttributeName,
4112 // NSBackgroundColorAttributeName, NSLanguageAttributeName.
4113 CFRetain(validAttributes);
4115 LOG(TextInput, "validAttributesForMarkedText -> (...)");
4116 return validAttributes;
4119 static Vector<WebCore::CompositionUnderline> extractUnderlines(NSAttributedString *string)
4121 Vector<WebCore::CompositionUnderline> result;
4122 int length = string.string.length;
4124 for (int i = 0; i < length;) {
4126 NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
4128 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {