2 * Copyright (C) 2015 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 "ViewGestureController.h"
50 #import "WKBrowsingContextControllerInternal.h"
51 #import "WKFullScreenWindowController.h"
52 #import "WKImmediateActionController.h"
53 #import "WKPrintingView.h"
54 #import "WKTextInputWindowController.h"
55 #import "WKViewLayoutStrategy.h"
57 #import "WebEditCommandProxy.h"
58 #import "WebEventFactory.h"
59 #import "WebInspectorProxy.h"
60 #import "WebPageProxy.h"
61 #import "WebProcessPool.h"
62 #import "WebProcessProxy.h"
63 #import "_WKRemoteObjectRegistryInternal.h"
64 #import "_WKThumbnailViewInternal.h"
65 #import <HIToolbox/CarbonEventsCore.h>
66 #import <WebCore/AXObjectCache.h>
67 #import <WebCore/ColorMac.h>
68 #import <WebCore/CoreGraphicsSPI.h>
69 #import <WebCore/DataDetectorsSPI.h>
70 #import <WebCore/DictionaryLookup.h>
71 #import <WebCore/DragData.h>
72 #import <WebCore/Editor.h>
73 #import <WebCore/KeypressCommand.h>
74 #import <WebCore/LocalizedStrings.h>
75 #import <WebCore/LookupSPI.h>
76 #import <WebCore/NSApplicationSPI.h>
77 #import <WebCore/NSImmediateActionGestureRecognizerSPI.h>
78 #import <WebCore/NSTextFinderSPI.h>
79 #import <WebCore/NSWindowSPI.h>
80 #import <WebCore/PlatformEventFactoryMac.h>
81 #import <WebCore/SoftLinking.h>
82 #import <WebCore/TextAlternativeWithRange.h>
83 #import <WebCore/TextUndoInsertionMarkupMac.h>
84 #import <WebCore/ViewState.h>
85 #import <WebCore/WebActionDisablingCALayerDelegate.h>
86 #import <WebCore/WebCoreCALayerExtras.h>
87 #import <WebCore/WebCoreFullScreenPlaceholderView.h>
88 #import <WebCore/WebCoreFullScreenWindow.h>
89 #import <WebCore/WebCoreNSStringExtras.h>
90 #import <WebKitSystemInterface.h>
93 SOFT_LINK_CONSTANT_MAY_FAIL(Lookup, LUNotificationPopoverWillClose, NSString *)
95 // FIXME: Move to an SPI header.
96 #if USE(ASYNC_NSTEXTINPUTCLIENT)
97 @interface NSTextInputContext (WKNSTextInputContextDetails)
98 - (void)handleEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler;
99 - (void)handleEventByInputMethod:(NSEvent *)event completionHandler:(void(^)(BOOL handled))completionHandler;
100 - (BOOL)handleEventByKeyboardLayout:(NSEvent *)event;
104 @interface WKWindowVisibilityObserver : NSObject {
106 WebKit::WebViewImpl *_impl;
109 - (instancetype)initWithView:(NSView *)view impl:(WebKit::WebViewImpl&)impl;
110 - (void)startObserving:(NSWindow *)window;
111 - (void)stopObserving:(NSWindow *)window;
112 - (void)startObservingFontPanel;
113 - (void)startObservingLookupDismissal;
116 @implementation WKWindowVisibilityObserver
118 - (instancetype)initWithView:(NSView *)view impl:(WebKit::WebViewImpl&)impl
127 NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
128 [workspaceNotificationCenter addObserver:self selector:@selector(_activeSpaceDidChange:) name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
135 if (canLoadLUNotificationPopoverWillClose())
136 [[NSNotificationCenter defaultCenter] removeObserver:self name:getLUNotificationPopoverWillClose() object:nil];
138 NSNotificationCenter *workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
139 [workspaceNotificationCenter removeObserver:self name:NSWorkspaceActiveSpaceDidChangeNotification object:nil];
144 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
145 static void* keyValueObservingContext = &keyValueObservingContext;
148 - (void)startObserving:(NSWindow *)window
153 NSNotificationCenter *defaultNotificationCenter = [NSNotificationCenter defaultCenter];
155 // An NSView derived object such as WKView cannot observe these notifications, because NSView itself observes them.
156 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidOrderOffScreen:) name:@"NSWindowDidOrderOffScreenNotification" object:window];
157 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidOrderOnScreen:) name:@"_NSWindowDidBecomeVisible" object:window];
159 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil];
160 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil];
161 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
162 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
163 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidMove:) name:NSWindowDidMoveNotification object:window];
164 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidResize:) name:NSWindowDidResizeNotification object:window];
165 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeBackingProperties:) name:NSWindowDidChangeBackingPropertiesNotification object:window];
166 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification object:window];
167 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeLayerHosting:) name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
168 [defaultNotificationCenter addObserver:self selector:@selector(_windowDidChangeOcclusionState:) name:NSWindowDidChangeOcclusionStateNotification object:window];
169 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
170 [window addObserver:self forKeyPath:@"contentLayoutRect" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
171 [window addObserver:self forKeyPath:@"titlebarAppearsTransparent" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext];
175 - (void)stopObserving:(NSWindow *)window
180 NSNotificationCenter *defaultNotificationCenter = [NSNotificationCenter defaultCenter];
182 [defaultNotificationCenter removeObserver:self name:@"NSWindowDidOrderOffScreenNotification" object:window];
183 [defaultNotificationCenter removeObserver:self name:@"_NSWindowDidBecomeVisible" object:window];
185 [defaultNotificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
186 [defaultNotificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
187 [defaultNotificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
188 [defaultNotificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
189 [defaultNotificationCenter removeObserver:self name:NSWindowDidMoveNotification object:window];
190 [defaultNotificationCenter removeObserver:self name:NSWindowDidResizeNotification object:window];
191 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window];
192 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeScreenNotification object:window];
193 [defaultNotificationCenter removeObserver:self name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window];
194 [defaultNotificationCenter removeObserver:self name:NSWindowDidChangeOcclusionStateNotification object:window];
195 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
196 if (_impl->isEditable())
197 [[NSFontPanel sharedFontPanel] removeObserver:self forKeyPath:@"visible" context:keyValueObservingContext];
198 [window removeObserver:self forKeyPath:@"contentLayoutRect" context:keyValueObservingContext];
199 [window removeObserver:self forKeyPath:@"titlebarAppearsTransparent" context:keyValueObservingContext];
203 - (void)startObservingFontPanel
205 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
206 [[NSFontPanel sharedFontPanel] addObserver:self forKeyPath:@"visible" options:0 context:keyValueObservingContext];
210 - (void)startObservingLookupDismissal
212 if (canLoadLUNotificationPopoverWillClose())
213 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_dictionaryLookupPopoverWillClose:) name:getLUNotificationPopoverWillClose() object:nil];
216 - (void)_windowDidOrderOnScreen:(NSNotification *)notification
218 _impl->windowDidOrderOnScreen();
221 - (void)_windowDidOrderOffScreen:(NSNotification *)notification
223 _impl->windowDidOrderOffScreen();
226 - (void)_windowDidBecomeKey:(NSNotification *)notification
228 _impl->windowDidBecomeKey([notification object]);
231 - (void)_windowDidResignKey:(NSNotification *)notification
233 _impl->windowDidResignKey([notification object]);
236 - (void)_windowDidMiniaturize:(NSNotification *)notification
238 _impl->windowDidMiniaturize();
241 - (void)_windowDidDeminiaturize:(NSNotification *)notification
243 _impl->windowDidDeminiaturize();
246 - (void)_windowDidMove:(NSNotification *)notification
248 _impl->windowDidMove();
251 - (void)_windowDidResize:(NSNotification *)notification
253 _impl->windowDidResize();
256 - (void)_windowDidChangeBackingProperties:(NSNotification *)notification
258 CGFloat oldBackingScaleFactor = [[notification.userInfo objectForKey:NSBackingPropertyOldScaleFactorKey] doubleValue];
259 _impl->windowDidChangeBackingProperties(oldBackingScaleFactor);
262 - (void)_windowDidChangeScreen:(NSNotification *)notification
264 _impl->windowDidChangeScreen();
267 - (void)_windowDidChangeLayerHosting:(NSNotification *)notification
269 _impl->windowDidChangeLayerHosting();
272 - (void)_windowDidChangeOcclusionState:(NSNotification *)notification
274 _impl->windowDidChangeOcclusionState();
277 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
279 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
280 if (context != keyValueObservingContext) {
281 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
285 if ([keyPath isEqualToString:@"visible"] && [NSFontPanel sharedFontPanelExists] && object == [NSFontPanel sharedFontPanel]) {
286 _impl->updateFontPanelIfNeeded();
289 if ([keyPath isEqualToString:@"contentLayoutRect"] || [keyPath isEqualToString:@"titlebarAppearsTransparent"])
290 _impl->updateContentInsetsIfAutomatic();
294 - (void)_dictionaryLookupPopoverWillClose:(NSNotification *)notification
296 _impl->clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::None);
299 - (void)_activeSpaceDidChange:(NSNotification *)notification
301 _impl->activeSpaceDidChange();
306 @interface WKEditCommandObjC : NSObject {
307 RefPtr<WebKit::WebEditCommandProxy> m_command;
309 - (id)initWithWebEditCommandProxy:(RefPtr<WebKit::WebEditCommandProxy>)command;
310 - (WebKit::WebEditCommandProxy*)command;
313 @interface WKEditorUndoTargetObjC : NSObject
314 - (void)undoEditing:(id)sender;
315 - (void)redoEditing:(id)sender;
318 @implementation WKEditCommandObjC
320 - (id)initWithWebEditCommandProxy:(RefPtr<WebKit::WebEditCommandProxy>)command
330 - (WebKit::WebEditCommandProxy*)command
332 return m_command.get();
337 @implementation WKEditorUndoTargetObjC
339 - (void)undoEditing:(id)sender
341 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
342 [sender command]->unapply();
345 - (void)redoEditing:(id)sender
347 ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
348 [sender command]->reapply();
353 @interface WKFlippedView : NSView
356 @implementation WKFlippedView
365 @interface WKResponderChainSink : NSResponder {
366 NSResponder *_lastResponderInChain;
367 bool _didReceiveUnhandledCommand;
370 - (id)initWithResponderChain:(NSResponder *)chain;
372 - (bool)didReceiveUnhandledCommand;
375 @implementation WKResponderChainSink
377 - (id)initWithResponderChain:(NSResponder *)chain
382 _lastResponderInChain = chain;
383 while (NSResponder *next = [_lastResponderInChain nextResponder])
384 _lastResponderInChain = next;
385 [_lastResponderInChain setNextResponder:self];
391 // This assumes that the responder chain was either unmodified since
392 // -initWithResponderChain: was called, or was modified in such a way
393 // that _lastResponderInChain is still in the chain, and self was not
394 // moved earlier in the chain than _lastResponderInChain.
395 NSResponder *responderBeforeSelf = _lastResponderInChain;
396 NSResponder *next = [responderBeforeSelf nextResponder];
397 for (; next && next != self; next = [next nextResponder])
398 responderBeforeSelf = next;
400 // Nothing to be done if we are no longer in the responder chain.
404 [responderBeforeSelf setNextResponder:[self nextResponder]];
405 _lastResponderInChain = nil;
408 - (bool)didReceiveUnhandledCommand
410 return _didReceiveUnhandledCommand;
413 - (void)noResponderFor:(SEL)selector
415 _didReceiveUnhandledCommand = true;
418 - (void)doCommandBySelector:(SEL)selector
420 _didReceiveUnhandledCommand = true;
423 - (BOOL)tryToPerform:(SEL)action with:(id)object
425 _didReceiveUnhandledCommand = true;
433 static NSTrackingAreaOptions trackingAreaOptions()
435 // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
436 NSTrackingAreaOptions options = NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingInVisibleRect | NSTrackingCursorUpdate;
437 if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy)
438 options |= NSTrackingActiveAlways;
440 options |= NSTrackingActiveInKeyWindow;
444 WebViewImpl::WebViewImpl(NSView <WebViewImplDelegate> *view, WKWebView *outerWebView, WebProcessPool& processPool, Ref<API::PageConfiguration>&& configuration)
446 , m_pageClient(std::make_unique<PageClientImpl>(view, outerWebView))
447 , m_page(processPool.createWebPage(*m_pageClient, WTF::move(configuration)))
448 , m_weakPtrFactory(this)
449 , m_needsViewFrameInWindowCoordinates(m_page->preferences().pluginsEnabled())
450 , m_intrinsicContentSize(CGSizeMake(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric))
451 , m_layoutStrategy([WKViewLayoutStrategy layoutStrategyWithPage:m_page view:m_view viewImpl:*this mode:kWKLayoutModeViewSize])
452 , m_undoTarget(adoptNS([[WKEditorUndoTargetObjC alloc] init]))
453 , m_windowVisibilityObserver(adoptNS([[WKWindowVisibilityObserver alloc] initWithView:view impl:*this]))
454 , m_primaryTrackingArea(adoptNS([[NSTrackingArea alloc] initWithRect:m_view.frame options:trackingAreaOptions() owner:m_view userInfo:nil]))
456 static_cast<PageClientImpl&>(*m_pageClient).setImpl(*this);
458 [NSApp registerServicesMenuSendTypes:PasteboardTypes::forSelection() returnTypes:PasteboardTypes::forEditing()];
460 [m_view addTrackingArea:m_primaryTrackingArea.get()];
462 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
464 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
465 if (Class gestureClass = NSClassFromString(@"NSImmediateActionGestureRecognizer")) {
466 m_immediateActionGestureRecognizer = adoptNS([(NSImmediateActionGestureRecognizer *)[gestureClass alloc] init]);
467 m_immediateActionController = adoptNS([[WKImmediateActionController alloc] initWithPage:m_page view:m_view viewImpl:*this recognizer:m_immediateActionGestureRecognizer.get()]);
468 [m_immediateActionGestureRecognizer setDelegate:m_immediateActionController.get()];
469 [m_immediateActionGestureRecognizer setDelaysPrimaryMouseButtonEvents:NO];
474 m_page->setAddsVisitedLinks(processPool.historyClient().addsVisitedLinks());
476 m_page->initializeWebPage();
478 registerDraggedTypes();
480 m_view.wantsLayer = YES;
482 // Explicitly set the layer contents placement so AppKit will make sure that our layer has masksToBounds set to YES.
483 m_view.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
485 WebProcessPool::statistics().wkViewCount++;
488 WebViewImpl::~WebViewImpl()
491 if (m_remoteObjectRegistry) {
492 m_page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID());
493 [m_remoteObjectRegistry _invalidate];
494 m_remoteObjectRegistry = nil;
498 ASSERT(!m_inSecureInputState);
501 ASSERT(!m_thumbnailView);
504 [m_layoutStrategy invalidate];
506 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
507 [m_immediateActionController willDestroyView:m_view];
512 WebProcessPool::statistics().wkViewCount--;
516 NSWindow *WebViewImpl::window()
518 return m_view.window;
521 void WebViewImpl::processDidExit()
523 notifyInputContextAboutDiscardedComposition();
525 if (m_layerHostingView)
526 setAcceleratedCompositingRootLayer(nil);
528 updateRemoteAccessibilityRegistration(false);
530 m_gestureController = nullptr;
533 void WebViewImpl::pageClosed()
535 updateRemoteAccessibilityRegistration(false);
538 void WebViewImpl::didRelaunchProcess()
540 accessibilityRegisterUIProcessTokens();
543 void WebViewImpl::setDrawsBackground(bool drawsBackground)
545 m_page->setDrawsBackground(drawsBackground);
548 bool WebViewImpl::drawsBackground() const
550 return m_page->drawsBackground();
553 bool WebViewImpl::isOpaque() const
555 return m_page->drawsBackground();
558 bool WebViewImpl::acceptsFirstResponder()
563 bool WebViewImpl::becomeFirstResponder()
565 // If we just became first responder again, there is no need to do anything,
566 // since resignFirstResponder has correctly detected this situation.
567 if (m_willBecomeFirstResponderAgain) {
568 m_willBecomeFirstResponderAgain = false;
572 NSSelectionDirection direction = [[m_view window] keyViewSelectionDirection];
574 m_inBecomeFirstResponder = true;
576 updateSecureInputState();
577 m_page->viewStateDidChange(WebCore::ViewState::IsFocused);
578 // Restore the selection in the editable region if resigning first responder cleared selection.
579 m_page->restoreSelectionInFocusedEditableElement();
581 m_inBecomeFirstResponder = false;
583 if (direction != NSDirectSelection) {
584 NSEvent *event = [NSApp currentEvent];
585 NSEvent *keyboardEvent = nil;
586 if ([event type] == NSKeyDown || [event type] == NSKeyUp)
587 keyboardEvent = event;
588 m_page->setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, { }), [](WebKit::CallbackBase::Error) { });
593 bool WebViewImpl::resignFirstResponder()
596 // Predict the case where we are losing first responder status only to
597 // gain it back again. We want resignFirstResponder to do nothing in that case.
598 id nextResponder = [[m_view window] _newFirstResponderAfterResigning];
600 // FIXME: This will probably need to change once WKWebView doesn't contain a WKView.
601 if ([nextResponder isKindOfClass:[WKWebView class]] && m_view.superview == nextResponder) {
602 m_willBecomeFirstResponderAgain = true;
607 m_willBecomeFirstResponderAgain = false;
608 m_inResignFirstResponder = true;
610 #if USE(ASYNC_NSTEXTINPUTCLIENT)
611 m_page->confirmCompositionAsync();
613 if (m_page->editorState().hasComposition && !m_page->editorState().shouldIgnoreCompositionSelectionChange)
614 m_page->cancelComposition();
617 notifyInputContextAboutDiscardedComposition();
619 resetSecureInputState();
621 if (!m_page->maintainsInactiveSelection())
622 m_page->clearSelection();
624 m_page->viewStateDidChange(WebCore::ViewState::IsFocused);
626 m_inResignFirstResponder = false;
631 bool WebViewImpl::isFocused() const
633 if (m_inBecomeFirstResponder)
635 if (m_inResignFirstResponder)
637 return m_view.window.firstResponder == m_view;
640 void WebViewImpl::viewWillStartLiveResize()
642 m_page->viewWillStartLiveResize();
644 [m_layoutStrategy willStartLiveResize];
647 void WebViewImpl::viewDidEndLiveResize()
649 m_page->viewWillEndLiveResize();
651 [m_layoutStrategy didEndLiveResize];
654 void WebViewImpl::renewGState()
656 if (m_textIndicatorWindow)
657 dismissContentRelativeChildWindowsWithAnimation(false);
659 // Update the view frame.
661 updateWindowAndViewFrames();
663 updateContentInsetsIfAutomatic();
666 void WebViewImpl::setFrameSize(CGSize)
668 [m_layoutStrategy didChangeFrameSize];
671 void WebViewImpl::disableFrameSizeUpdates()
673 [m_layoutStrategy disableFrameSizeUpdates];
676 void WebViewImpl::enableFrameSizeUpdates()
678 [m_layoutStrategy enableFrameSizeUpdates];
681 bool WebViewImpl::frameSizeUpdatesDisabled() const
683 return [m_layoutStrategy frameSizeUpdatesDisabled];
686 void WebViewImpl::setFrameAndScrollBy(CGRect frame, CGSize offset)
688 ASSERT(CGSizeEqualToSize(m_resizeScrollOffset, CGSizeZero));
690 m_resizeScrollOffset = offset;
691 m_view.frame = NSRectFromCGRect(frame);
694 void WebViewImpl::updateWindowAndViewFrames()
696 if (clipsToVisibleRect())
697 updateViewExposedRect();
699 if (m_didScheduleWindowAndViewFrameUpdate)
702 m_didScheduleWindowAndViewFrameUpdate = true;
704 auto weakThis = createWeakPtr();
705 dispatch_async(dispatch_get_main_queue(), [weakThis] {
709 weakThis->m_didScheduleWindowAndViewFrameUpdate = false;
711 NSRect viewFrameInWindowCoordinates = NSZeroRect;
712 NSPoint accessibilityPosition = NSZeroPoint;
714 if (weakThis->m_needsViewFrameInWindowCoordinates)
715 viewFrameInWindowCoordinates = [weakThis->m_view convertRect:weakThis->m_view.frame toView:nil];
717 #pragma clang diagnostic push
718 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
719 if (WebCore::AXObjectCache::accessibilityEnabled())
720 accessibilityPosition = [[weakThis->m_view accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
721 #pragma clang diagnostic pop
723 weakThis->m_page->windowAndViewFramesChanged(viewFrameInWindowCoordinates, accessibilityPosition);
727 void WebViewImpl::setFixedLayoutSize(CGSize fixedLayoutSize)
729 m_page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
732 CGSize WebViewImpl::fixedLayoutSize() const
734 return m_page->fixedLayoutSize();
737 std::unique_ptr<WebKit::DrawingAreaProxy> WebViewImpl::createDrawingAreaProxy()
739 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"WebKit2UseRemoteLayerTreeDrawingArea"] boolValue])
740 return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(m_page);
742 return std::make_unique<TiledCoreAnimationDrawingAreaProxy>(m_page);
745 bool WebViewImpl::isUsingUISideCompositing() const
747 auto* drawingArea = m_page->drawingArea();
748 return drawingArea && drawingArea->type() == DrawingAreaTypeRemoteLayerTree;
751 void WebViewImpl::setDrawingAreaSize(CGSize size)
753 if (!m_page->drawingArea())
756 m_page->drawingArea()->setSize(WebCore::IntSize(size), WebCore::IntSize(), WebCore::IntSize(m_resizeScrollOffset));
757 m_resizeScrollOffset = CGSizeZero;
760 #pragma clang diagnostic push
761 #pragma clang diagnostic ignored "-Wmissing-noreturn"
762 // This method forces a drawing area geometry update, even if frame size updates are disabled.
763 // The updated is performed asynchronously; we don't wait for the geometry update before returning.
764 // The area drawn need not match the current frame size - if it differs it will be anchored to the
765 // frame according to the current contentAnchor.
766 void WebViewImpl::forceAsyncDrawingAreaSizeUpdate(CGSize size)
768 // This SPI is only used on 10.9 and below, and is incompatible with the fence-based drawing area size synchronization in 10.10+.
769 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
770 if (m_clipsToVisibleRect)
771 updateViewExposedRect();
772 setDrawingAreaSize(size);
774 // If a geometry update is pending the new update won't be sent. Poll without waiting for any
775 // pending did-update message now, such that the new update can be sent. We do so after setting
776 // the drawing area size such that the latest update is sent.
777 if (DrawingAreaProxy* drawingArea = m_page->drawingArea())
778 drawingArea->waitForPossibleGeometryUpdate(std::chrono::milliseconds::zero());
780 ASSERT_NOT_REACHED();
784 void WebViewImpl::waitForAsyncDrawingAreaSizeUpdate()
786 // This SPI is only used on 10.9 and below, and is incompatible with the fence-based drawing area size synchronization in 10.10+.
787 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
788 if (DrawingAreaProxy* drawingArea = m_page->drawingArea()) {
789 // If a geometry update is still pending then the action of receiving the
790 // first geometry update may result in another update being scheduled -
791 // we should wait for this to complete too.
792 drawingArea->waitForPossibleGeometryUpdate(DrawingAreaProxy::didUpdateBackingStoreStateTimeout() / 2);
793 drawingArea->waitForPossibleGeometryUpdate(DrawingAreaProxy::didUpdateBackingStoreStateTimeout() / 2);
796 ASSERT_NOT_REACHED();
799 #pragma clang diagnostic pop
801 void WebViewImpl::updateLayer()
803 m_view.layer.backgroundColor = CGColorGetConstantColor(drawsBackground() ? kCGColorWhite : kCGColorClear);
805 // If asynchronous geometry updates have been sent by forceAsyncDrawingAreaSizeUpdate,
806 // then subsequent calls to setFrameSize should not result in us waiting for the did
807 // udpate response if setFrameSize is called.
808 if (frameSizeUpdatesDisabled())
811 if (DrawingAreaProxy* drawingArea = m_page->drawingArea())
812 drawingArea->waitForPossibleGeometryUpdate();
815 void WebViewImpl::drawRect(CGRect rect)
817 LOG(Printing, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
818 m_page->endPrinting();
821 bool WebViewImpl::canChangeFrameLayout(WebFrameProxy& frame)
823 // PDF documents are already paginated, so we can't change them to add headers and footers.
824 return !frame.isDisplayingPDFDocument();
827 NSPrintOperation *WebViewImpl::printOperationWithPrintInfo(NSPrintInfo *printInfo, WebFrameProxy& frame)
829 LOG(Printing, "Creating an NSPrintOperation for frame '%s'", frame.url().utf8().data());
831 // FIXME: If the frame cannot be printed (e.g. if it contains an encrypted PDF that disallows
832 // printing), this function should return nil.
833 RetainPtr<WKPrintingView> printingView = adoptNS([[WKPrintingView alloc] initWithFrameProxy:&frame view:m_view]);
834 // NSPrintOperation takes ownership of the view.
835 NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get() printInfo:printInfo];
836 [printOperation setCanSpawnSeparateThread:YES];
837 [printOperation setJobTitle:frame.title()];
838 printingView->_printOperation = printOperation;
839 return printOperation;
842 void WebViewImpl::setAutomaticallyAdjustsContentInsets(bool automaticallyAdjustsContentInsets)
844 m_automaticallyAdjustsContentInsets = automaticallyAdjustsContentInsets;
845 updateContentInsetsIfAutomatic();
848 void WebViewImpl::updateContentInsetsIfAutomatic()
850 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
851 if (!m_automaticallyAdjustsContentInsets)
854 NSWindow *window = m_view.window;
855 if ((window.styleMask & NSFullSizeContentViewWindowMask) && !window.titlebarAppearsTransparent && ![m_view enclosingScrollView]) {
856 NSRect contentLayoutRect = [m_view convertRect:window.contentLayoutRect fromView:nil];
857 CGFloat newTopContentInset = NSMaxY(contentLayoutRect) - NSHeight(contentLayoutRect);
858 if (m_topContentInset != newTopContentInset)
859 setTopContentInset(newTopContentInset);
861 setTopContentInset(0);
865 void WebViewImpl::setTopContentInset(CGFloat contentInset)
867 m_topContentInset = contentInset;
869 if (m_didScheduleSetTopContentInset)
872 m_didScheduleSetTopContentInset = true;
874 auto weakThis = createWeakPtr();
875 dispatch_async(dispatch_get_main_queue(), [weakThis] {
878 weakThis->dispatchSetTopContentInset();
882 void WebViewImpl::dispatchSetTopContentInset()
884 if (!m_didScheduleSetTopContentInset)
887 m_didScheduleSetTopContentInset = false;
888 m_page->setTopContentInset(m_topContentInset);
891 void WebViewImpl::prepareContentInRect(CGRect rect)
893 m_contentPreparationRect = rect;
894 m_useContentPreparationRectForVisibleRect = true;
896 updateViewExposedRect();
899 void WebViewImpl::updateViewExposedRect()
901 CGRect exposedRect = NSRectToCGRect([m_view visibleRect]);
903 if (m_useContentPreparationRectForVisibleRect)
904 exposedRect = CGRectUnion(m_contentPreparationRect, exposedRect);
906 if (auto drawingArea = m_page->drawingArea())
907 drawingArea->setExposedRect(m_clipsToVisibleRect ? WebCore::FloatRect(exposedRect) : WebCore::FloatRect::infiniteRect());
910 void WebViewImpl::setClipsToVisibleRect(bool clipsToVisibleRect)
912 m_clipsToVisibleRect = clipsToVisibleRect;
913 updateViewExposedRect();
916 void WebViewImpl::setMinimumSizeForAutoLayout(CGSize minimumSizeForAutoLayout)
918 bool expandsToFit = minimumSizeForAutoLayout.width > 0;
920 m_page->setMinimumLayoutSize(WebCore::IntSize(minimumSizeForAutoLayout));
921 m_page->setMainFrameIsScrollable(!expandsToFit);
923 setClipsToVisibleRect(expandsToFit);
926 CGSize WebViewImpl::minimumSizeForAutoLayout() const
928 return m_page->minimumLayoutSize();
931 void WebViewImpl::setShouldExpandToViewHeightForAutoLayout(bool shouldExpandToViewHeightForAutoLayout)
933 m_page->setAutoSizingShouldExpandToViewHeight(shouldExpandToViewHeightForAutoLayout);
936 bool WebViewImpl::shouldExpandToViewHeightForAutoLayout() const
938 return m_page->autoSizingShouldExpandToViewHeight();
941 void WebViewImpl::setIntrinsicContentSize(CGSize intrinsicContentSize)
943 // If the intrinsic content size is less than the minimum layout width, the content flowed to fit,
944 // so we can report that that dimension is flexible. If not, we need to report our intrinsic width
945 // so that autolayout will know to provide space for us.
947 CGSize intrinsicContentSizeAcknowledgingFlexibleWidth = intrinsicContentSize;
948 if (intrinsicContentSize.width < m_page->minimumLayoutSize().width())
949 intrinsicContentSizeAcknowledgingFlexibleWidth.width = NSViewNoInstrinsicMetric;
951 m_intrinsicContentSize = intrinsicContentSizeAcknowledgingFlexibleWidth;
952 [m_view invalidateIntrinsicContentSize];
955 CGSize WebViewImpl::intrinsicContentSize() const
957 return m_intrinsicContentSize;
960 void WebViewImpl::setViewScale(CGFloat viewScale)
962 m_lastRequestedViewScale = viewScale;
964 if (!supportsArbitraryLayoutModes() && viewScale != 1)
967 if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
968 [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
970 m_page->scaleView(viewScale);
971 [m_layoutStrategy didChangeViewScale];
974 CGFloat WebViewImpl::viewScale() const
976 return m_page->viewScaleFactor();
979 WKLayoutMode WebViewImpl::layoutMode() const
981 return [m_layoutStrategy layoutMode];
984 void WebViewImpl::setLayoutMode(WKLayoutMode layoutMode)
986 m_lastRequestedLayoutMode = layoutMode;
988 if (!supportsArbitraryLayoutModes() && layoutMode != kWKLayoutModeViewSize)
991 if (layoutMode == [m_layoutStrategy layoutMode])
994 [m_layoutStrategy willChangeLayoutStrategy];
995 m_layoutStrategy = [WKViewLayoutStrategy layoutStrategyWithPage:m_page view:m_view viewImpl:*this mode:layoutMode];
998 bool WebViewImpl::supportsArbitraryLayoutModes() const
1000 if ([m_fullScreenWindowController isFullScreen])
1003 WebFrameProxy* frame = m_page->mainFrame();
1007 // If we have a plugin document in the main frame, avoid using custom WKLayoutModes
1008 // and fall back to the defaults, because there's a good chance that it won't work (e.g. with PDFPlugin).
1009 if (frame->containsPluginDocument())
1015 void WebViewImpl::updateSupportsArbitraryLayoutModes()
1017 if (!supportsArbitraryLayoutModes()) {
1018 WKLayoutMode oldRequestedLayoutMode = m_lastRequestedLayoutMode;
1019 CGFloat oldRequestedViewScale = m_lastRequestedViewScale;
1021 setLayoutMode(kWKLayoutModeViewSize);
1023 // The 'last requested' parameters will have been overwritten by setting them above, but we don't
1024 // want this to count as a request (only changes from the client count), so reset them.
1025 m_lastRequestedLayoutMode = oldRequestedLayoutMode;
1026 m_lastRequestedViewScale = oldRequestedViewScale;
1027 } else if (m_lastRequestedLayoutMode != [m_layoutStrategy layoutMode]) {
1028 setViewScale(m_lastRequestedViewScale);
1029 setLayoutMode(m_lastRequestedLayoutMode);
1033 void WebViewImpl::setOverrideDeviceScaleFactor(CGFloat deviceScaleFactor)
1035 m_overrideDeviceScaleFactor = deviceScaleFactor;
1036 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1039 float WebViewImpl::intrinsicDeviceScaleFactor() const
1041 if (m_overrideDeviceScaleFactor)
1042 return m_overrideDeviceScaleFactor;
1043 if (m_targetWindowForMovePreparation)
1044 return m_targetWindowForMovePreparation.backingScaleFactor;
1045 if (NSWindow *window = m_view.window)
1046 return window.backingScaleFactor;
1047 return [NSScreen mainScreen].backingScaleFactor;
1050 void WebViewImpl::windowDidOrderOffScreen()
1052 m_page->viewStateDidChange(WebCore::ViewState::IsVisible | WebCore::ViewState::WindowIsActive);
1055 void WebViewImpl::windowDidOrderOnScreen()
1057 m_page->viewStateDidChange(WebCore::ViewState::IsVisible | WebCore::ViewState::WindowIsActive);
1060 void WebViewImpl::windowDidBecomeKey(NSWindow *keyWindow)
1062 if (keyWindow == m_view.window || keyWindow == m_view.window.attachedSheet) {
1063 updateSecureInputState();
1064 m_page->viewStateDidChange(WebCore::ViewState::WindowIsActive);
1068 void WebViewImpl::windowDidResignKey(NSWindow *formerKeyWindow)
1070 if (formerKeyWindow == m_view.window || formerKeyWindow == m_view.window.attachedSheet) {
1071 updateSecureInputState();
1072 m_page->viewStateDidChange(WebCore::ViewState::WindowIsActive);
1076 void WebViewImpl::windowDidMiniaturize()
1078 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1081 void WebViewImpl::windowDidDeminiaturize()
1083 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1086 void WebViewImpl::windowDidMove()
1088 updateWindowAndViewFrames();
1091 void WebViewImpl::windowDidResize()
1093 updateWindowAndViewFrames();
1096 void WebViewImpl::windowDidChangeBackingProperties(CGFloat oldBackingScaleFactor)
1098 CGFloat newBackingScaleFactor = intrinsicDeviceScaleFactor();
1099 if (oldBackingScaleFactor == newBackingScaleFactor)
1102 m_page->setIntrinsicDeviceScaleFactor(newBackingScaleFactor);
1105 void WebViewImpl::windowDidChangeScreen()
1107 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1108 m_page->windowScreenDidChange((PlatformDisplayID)[[[[window screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue]);
1111 void WebViewImpl::windowDidChangeLayerHosting()
1113 m_page->layerHostingModeDidChange();
1116 void WebViewImpl::windowDidChangeOcclusionState()
1118 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1121 bool WebViewImpl::mightBeginDragWhileInactive()
1123 if (m_view.window.isKeyWindow)
1126 if (m_page->editorState().selectionIsNone || !m_page->editorState().selectionIsRange)
1132 bool WebViewImpl::mightBeginScrollWhileInactive()
1134 // Legacy style scrollbars have design details that rely on tracking the mouse all the time.
1135 if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy)
1141 bool WebViewImpl::acceptsFirstMouse(NSEvent *event)
1143 if (!mightBeginDragWhileInactive() && !mightBeginScrollWhileInactive())
1146 // There's a chance that responding to this event will run a nested event loop, and
1147 // fetching a new event might release the old one. Retaining and then autoreleasing
1148 // the current event prevents that from causing a problem inside WebKit or AppKit code.
1149 [[event retain] autorelease];
1151 if (![m_view hitTest:event.locationInWindow])
1154 setLastMouseDownEvent(event);
1155 bool result = m_page->acceptsFirstMouse(event.eventNumber, WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
1156 setLastMouseDownEvent(nil);
1160 bool WebViewImpl::shouldDelayWindowOrderingForEvent(NSEvent *event)
1162 if (!mightBeginDragWhileInactive())
1165 // There's a chance that responding to this event will run a nested event loop, and
1166 // fetching a new event might release the old one. Retaining and then autoreleasing
1167 // the current event prevents that from causing a problem inside WebKit or AppKit code.
1168 [[event retain] autorelease];
1170 if (![m_view hitTest:event.locationInWindow])
1173 setLastMouseDownEvent(event);
1174 bool result = m_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
1175 setLastMouseDownEvent(nil);
1179 bool WebViewImpl::windowResizeMouseLocationIsInVisibleScrollerThumb(CGPoint point)
1181 NSPoint localPoint = [m_view convertPoint:NSPointFromCGPoint(point) fromView:nil];
1182 NSRect visibleThumbRect = NSRect(m_page->visibleScrollerThumbRect());
1183 return NSMouseInRect(localPoint, visibleThumbRect, m_view.isFlipped);
1186 void WebViewImpl::viewWillMoveToWindow(NSWindow *window)
1188 // If we're in the middle of preparing to move to a window, we should only be moved to that window.
1189 ASSERT(!m_targetWindowForMovePreparation || (m_targetWindowForMovePreparation == window));
1191 NSWindow *currentWindow = m_view.window;
1192 if (window == currentWindow)
1195 clearAllEditCommands();
1197 NSWindow *stopObservingWindow = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1198 [m_windowVisibilityObserver stopObserving:stopObservingWindow];
1199 [m_windowVisibilityObserver startObserving:window];
1202 void WebViewImpl::viewDidMoveToWindow()
1204 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1207 windowDidChangeScreen();
1209 WebCore::ViewState::Flags viewStateChanges = WebCore::ViewState::WindowIsActive | WebCore::ViewState::IsVisible;
1210 if (m_isDeferringViewInWindowChanges)
1211 m_viewInWindowChangeWasDeferred = true;
1213 viewStateChanges |= WebCore::ViewState::IsInWindow;
1214 m_page->viewStateDidChange(viewStateChanges);
1216 updateWindowAndViewFrames();
1218 // FIXME(135509) This call becomes unnecessary once 135509 is fixed; remove.
1219 m_page->layerHostingModeDidChange();
1221 if (!m_flagsChangedEventMonitor) {
1222 auto weakThis = createWeakPtr();
1223 m_flagsChangedEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSFlagsChangedMask handler:[weakThis] (NSEvent *flagsChangedEvent) {
1225 weakThis->postFakeMouseMovedEventForFlagsChangedEvent(flagsChangedEvent);
1226 return flagsChangedEvent;
1230 accessibilityRegisterUIProcessTokens();
1232 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1233 if (m_immediateActionGestureRecognizer && ![[m_view gestureRecognizers] containsObject:m_immediateActionGestureRecognizer.get()] && !m_ignoresNonWheelEvents && m_allowsLinkPreview)
1234 [m_view addGestureRecognizer:m_immediateActionGestureRecognizer.get()];
1237 WebCore::ViewState::Flags viewStateChanges = WebCore::ViewState::WindowIsActive | WebCore::ViewState::IsVisible;
1238 if (m_isDeferringViewInWindowChanges)
1239 m_viewInWindowChangeWasDeferred = true;
1241 viewStateChanges |= WebCore::ViewState::IsInWindow;
1242 m_page->viewStateDidChange(viewStateChanges);
1244 [NSEvent removeMonitor:m_flagsChangedEventMonitor];
1245 m_flagsChangedEventMonitor = nil;
1247 dismissContentRelativeChildWindowsWithAnimation(false);
1249 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1250 if (m_immediateActionGestureRecognizer)
1251 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
1255 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1258 void WebViewImpl::viewDidChangeBackingProperties()
1260 NSColorSpace *colorSpace = m_view.window.colorSpace;
1261 if ([colorSpace isEqualTo:m_colorSpace.get()])
1264 m_colorSpace = nullptr;
1265 if (DrawingAreaProxy *drawingArea = m_page->drawingArea())
1266 drawingArea->colorSpaceDidChange();
1269 void WebViewImpl::viewDidHide()
1271 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1274 void WebViewImpl::viewDidUnhide()
1276 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1279 void WebViewImpl::activeSpaceDidChange()
1281 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1284 NSView *WebViewImpl::hitTest(CGPoint point)
1286 NSView *hitView = [m_view _web_superHitTest:NSPointFromCGPoint(point)];
1287 if (hitView && hitView == m_layerHostingView)
1293 void WebViewImpl::postFakeMouseMovedEventForFlagsChangedEvent(NSEvent *flagsChangedEvent)
1295 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved location:flagsChangedEvent.window.mouseLocationOutsideOfEventStream
1296 modifierFlags:flagsChangedEvent.modifierFlags timestamp:flagsChangedEvent.timestamp windowNumber:flagsChangedEvent.windowNumber
1297 context:flagsChangedEvent.context eventNumber:0 clickCount:0 pressure:0];
1298 NativeWebMouseEvent webEvent(fakeEvent, m_lastPressureEvent.get(), m_view);
1299 m_page->handleMouseEvent(webEvent);
1302 ColorSpaceData WebViewImpl::colorSpace()
1304 if (!m_colorSpace) {
1305 if (m_targetWindowForMovePreparation)
1306 m_colorSpace = m_targetWindowForMovePreparation.colorSpace;
1307 else if (NSWindow *window = m_view.window)
1308 m_colorSpace = window.colorSpace;
1310 m_colorSpace = [NSScreen mainScreen].colorSpace;
1313 ColorSpaceData colorSpaceData;
1314 colorSpaceData.cgColorSpace = [m_colorSpace CGColorSpace];
1316 return colorSpaceData;
1319 void WebViewImpl::setUnderlayColor(NSColor *underlayColor)
1321 m_page->setUnderlayColor(WebCore::colorFromNSColor(underlayColor));
1324 NSColor *WebViewImpl::underlayColor() const
1326 WebCore::Color webColor = m_page->underlayColor();
1327 if (!webColor.isValid())
1330 return WebCore::nsColor(webColor);
1333 NSColor *WebViewImpl::pageExtendedBackgroundColor() const
1335 WebCore::Color color = m_page->pageExtendedBackgroundColor();
1336 if (!color.isValid())
1339 return WebCore::nsColor(color);
1342 void WebViewImpl::setOverlayScrollbarStyle(WTF::Optional<WebCore::ScrollbarOverlayStyle> scrollbarStyle)
1344 m_page->setOverlayScrollbarStyle(scrollbarStyle);
1347 WTF::Optional<WebCore::ScrollbarOverlayStyle> WebViewImpl::overlayScrollbarStyle() const
1349 return m_page->overlayScrollbarStyle();
1352 void WebViewImpl::beginDeferringViewInWindowChanges()
1354 if (m_shouldDeferViewInWindowChanges) {
1355 NSLog(@"beginDeferringViewInWindowChanges was called while already deferring view-in-window changes!");
1359 m_shouldDeferViewInWindowChanges = true;
1362 void WebViewImpl::endDeferringViewInWindowChanges()
1364 if (!m_shouldDeferViewInWindowChanges) {
1365 NSLog(@"endDeferringViewInWindowChanges was called without beginDeferringViewInWindowChanges!");
1369 m_shouldDeferViewInWindowChanges = false;
1371 if (m_viewInWindowChangeWasDeferred) {
1372 dispatchSetTopContentInset();
1373 m_page->viewStateDidChange(WebCore::ViewState::IsInWindow);
1374 m_viewInWindowChangeWasDeferred = false;
1378 void WebViewImpl::endDeferringViewInWindowChangesSync()
1380 if (!m_shouldDeferViewInWindowChanges) {
1381 NSLog(@"endDeferringViewInWindowChangesSync was called without beginDeferringViewInWindowChanges!");
1385 m_shouldDeferViewInWindowChanges = false;
1387 if (m_viewInWindowChangeWasDeferred) {
1388 dispatchSetTopContentInset();
1389 m_page->viewStateDidChange(WebCore::ViewState::IsInWindow);
1390 m_viewInWindowChangeWasDeferred = false;
1394 void WebViewImpl::prepareForMoveToWindow(NSWindow *targetWindow, std::function<void()> completionHandler)
1396 m_shouldDeferViewInWindowChanges = true;
1397 viewWillMoveToWindow(targetWindow);
1398 m_targetWindowForMovePreparation = targetWindow;
1399 viewDidMoveToWindow();
1401 m_shouldDeferViewInWindowChanges = false;
1403 auto weakThis = createWeakPtr();
1404 m_page->installViewStateChangeCompletionHandler([weakThis, completionHandler]() {
1405 completionHandler();
1410 ASSERT(weakThis->m_view.window == weakThis->m_targetWindowForMovePreparation);
1411 weakThis->m_targetWindowForMovePreparation = nil;
1414 dispatchSetTopContentInset();
1415 m_page->viewStateDidChange(WebCore::ViewState::IsInWindow, false, WebPageProxy::ViewStateChangeDispatchMode::Immediate);
1416 m_viewInWindowChangeWasDeferred = false;
1419 void WebViewImpl::updateSecureInputState()
1421 if (![[m_view window] isKeyWindow] || !isFocused()) {
1422 if (m_inSecureInputState) {
1423 DisableSecureEventInput();
1424 m_inSecureInputState = false;
1428 // WKView has a single input context for all editable areas (except for plug-ins).
1429 NSTextInputContext *context = [m_view _web_superInputContext];
1430 bool isInPasswordField = m_page->editorState().isInPasswordField;
1432 if (isInPasswordField) {
1433 if (!m_inSecureInputState)
1434 EnableSecureEventInput();
1435 static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1];
1436 LOG(TextInput, "-> setAllowedInputSourceLocales:romanInputSources");
1437 [context setAllowedInputSourceLocales:romanInputSources];
1439 if (m_inSecureInputState)
1440 DisableSecureEventInput();
1441 LOG(TextInput, "-> setAllowedInputSourceLocales:nil");
1442 [context setAllowedInputSourceLocales:nil];
1444 m_inSecureInputState = isInPasswordField;
1447 void WebViewImpl::resetSecureInputState()
1449 if (m_inSecureInputState) {
1450 DisableSecureEventInput();
1451 m_inSecureInputState = false;
1455 void WebViewImpl::notifyInputContextAboutDiscardedComposition()
1457 // <rdar://problem/9359055>: -discardMarkedText can only be called for active contexts.
1458 // FIXME: We fail to ever notify the input context if something (e.g. a navigation) happens while the window is not key.
1459 // This is not a problem when the window is key, because we discard marked text on resigning first responder.
1460 if (![[m_view window] isKeyWindow] || m_view != [[m_view window] firstResponder])
1463 LOG(TextInput, "-> discardMarkedText");
1465 [[m_view _web_superInputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to.
1468 void WebViewImpl::setPluginComplexTextInputState(PluginComplexTextInputState pluginComplexTextInputState)
1470 m_pluginComplexTextInputState = pluginComplexTextInputState;
1472 if (m_pluginComplexTextInputState != PluginComplexTextInputDisabled)
1475 // Send back an empty string to the plug-in. This will disable text input.
1476 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, String());
1479 void WebViewImpl::setPluginComplexTextInputStateAndIdentifier(PluginComplexTextInputState pluginComplexTextInputState, uint64_t pluginComplexTextInputIdentifier)
1481 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier) {
1482 // We're asked to update the state for a plug-in that doesn't have focus.
1486 setPluginComplexTextInputState(pluginComplexTextInputState);
1489 void WebViewImpl::disableComplexTextInputIfNecessary()
1491 if (!m_pluginComplexTextInputIdentifier)
1494 if (m_pluginComplexTextInputState != PluginComplexTextInputEnabled)
1497 // Check if the text input window has been dismissed.
1498 if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
1499 setPluginComplexTextInputState(PluginComplexTextInputDisabled);
1502 bool WebViewImpl::handlePluginComplexTextInputKeyDown(NSEvent *event)
1504 ASSERT(m_pluginComplexTextInputIdentifier);
1505 ASSERT(m_pluginComplexTextInputState != PluginComplexTextInputDisabled);
1507 BOOL usingLegacyCocoaTextInput = m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
1509 NSString *string = nil;
1510 BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string];
1513 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, string);
1515 if (!usingLegacyCocoaTextInput)
1516 m_pluginComplexTextInputState = PluginComplexTextInputDisabled;
1519 return didHandleEvent;
1522 bool WebViewImpl::tryHandlePluginComplexTextInputKeyDown(NSEvent *event)
1524 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
1527 // Check if the text input window has been dismissed and let the plug-in process know.
1528 // This is only valid with the updated Cocoa text input spec.
1529 disableComplexTextInputIfNecessary();
1531 // Try feeding the keyboard event directly to the plug-in.
1532 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
1533 return handlePluginComplexTextInputKeyDown(event);
1538 void WebViewImpl::pluginFocusOrWindowFocusChanged(bool pluginHasFocusAndWindowHasFocus, uint64_t pluginComplexTextInputIdentifier)
1540 BOOL inputSourceChanged = m_pluginComplexTextInputIdentifier;
1542 if (pluginHasFocusAndWindowHasFocus) {
1543 // Check if we're already allowing text input for this plug-in.
1544 if (pluginComplexTextInputIdentifier == m_pluginComplexTextInputIdentifier)
1547 m_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier;
1550 // Check if we got a request to unfocus a plug-in that isn't focused.
1551 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier)
1554 m_pluginComplexTextInputIdentifier = 0;
1557 if (inputSourceChanged) {
1558 // The input source changed; discard any entered text.
1559 [[WKTextInputWindowController sharedTextInputWindowController] unmarkText];
1562 // This will force the current input context to be updated to its correct value.
1563 [NSApp updateWindows];
1566 bool WebViewImpl::tryPostProcessPluginComplexTextInputKeyDown(NSEvent *event)
1568 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
1571 // In the legacy text input model, the event has already been sent to the input method.
1572 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
1575 return handlePluginComplexTextInputKeyDown(event);
1578 void WebViewImpl::handleAcceptedAlternativeText(const String& acceptedAlternative)
1580 m_page->handleAlternativeTextUIResult(acceptedAlternative);
1584 NSInteger WebViewImpl::spellCheckerDocumentTag()
1586 if (!m_spellCheckerDocumentTag)
1587 m_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
1588 return m_spellCheckerDocumentTag.value();
1591 void WebViewImpl::pressureChangeWithEvent(NSEvent *event)
1593 #if defined(__LP64__) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101003
1594 if (event == m_lastPressureEvent)
1597 if (m_ignoresNonWheelEvents)
1600 if (event.phase != NSEventPhaseChanged && event.phase != NSEventPhaseBegan && event.phase != NSEventPhaseEnded)
1603 NativeWebMouseEvent webEvent(event, m_lastPressureEvent.get(), m_view);
1604 m_page->handleMouseEvent(webEvent);
1606 m_lastPressureEvent = event;
1610 #if ENABLE(FULLSCREEN_API)
1611 bool WebViewImpl::hasFullScreenWindowController() const
1613 return !!m_fullScreenWindowController;
1616 WKFullScreenWindowController *WebViewImpl::fullScreenWindowController()
1618 if (!m_fullScreenWindowController)
1619 m_fullScreenWindowController = adoptNS([[WKFullScreenWindowController alloc] initWithWindow:createFullScreenWindow() webView:m_view page:m_page]);
1621 return m_fullScreenWindowController.get();
1624 void WebViewImpl::closeFullScreenWindowController()
1626 if (!m_fullScreenWindowController)
1629 [m_fullScreenWindowController close];
1630 m_fullScreenWindowController = nullptr;
1634 NSView *WebViewImpl::fullScreenPlaceholderView()
1636 #if ENABLE(FULLSCREEN_API)
1637 if (m_fullScreenWindowController && [m_fullScreenWindowController isFullScreen])
1638 return [m_fullScreenWindowController webViewPlaceholder];
1643 NSWindow *WebViewImpl::createFullScreenWindow()
1645 #if ENABLE(FULLSCREEN_API)
1646 return [[[WebCoreFullScreenWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] styleMask:(NSBorderlessWindowMask | NSResizableWindowMask) backing:NSBackingStoreBuffered defer:NO] autorelease];
1652 bool WebViewImpl::isEditable() const
1654 return m_page->isEditable();
1657 typedef HashMap<SEL, String> SelectorNameMap;
1659 // Map selectors into Editor command names.
1660 // This is not needed for any selectors that have the same name as the Editor command.
1661 static const SelectorNameMap& selectorExceptionMap()
1663 static NeverDestroyed<SelectorNameMap> map;
1665 struct SelectorAndCommandName {
1667 ASCIILiteral commandName;
1670 static const SelectorAndCommandName names[] = {
1671 { @selector(insertNewlineIgnoringFieldEditor:), ASCIILiteral("InsertNewline") },
1672 { @selector(insertParagraphSeparator:), ASCIILiteral("InsertNewline") },
1673 { @selector(insertTabIgnoringFieldEditor:), ASCIILiteral("InsertTab") },
1674 { @selector(pageDown:), ASCIILiteral("MovePageDown") },
1675 { @selector(pageDownAndModifySelection:), ASCIILiteral("MovePageDownAndModifySelection") },
1676 { @selector(pageUp:), ASCIILiteral("MovePageUp") },
1677 { @selector(pageUpAndModifySelection:), ASCIILiteral("MovePageUpAndModifySelection") },
1678 { @selector(scrollPageDown:), ASCIILiteral("ScrollPageForward") },
1679 { @selector(scrollPageUp:), ASCIILiteral("ScrollPageBackward") }
1682 for (auto& name : names)
1683 map.get().add(name.selector, name.commandName);
1688 static String commandNameForSelector(SEL selector)
1690 // Check the exception map first.
1691 static const SelectorNameMap& exceptionMap = selectorExceptionMap();
1692 SelectorNameMap::const_iterator it = exceptionMap.find(selector);
1693 if (it != exceptionMap.end())
1696 // Remove the trailing colon.
1697 // No need to capitalize the command name since Editor command names are
1698 // not case sensitive.
1699 const char* selectorName = sel_getName(selector);
1700 size_t selectorNameLength = strlen(selectorName);
1701 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
1703 return String(selectorName, selectorNameLength - 1);
1706 bool WebViewImpl::executeSavedCommandBySelector(SEL selector)
1708 LOG(TextInput, "Executing previously saved command %s", sel_getName(selector));
1709 // The sink does two things: 1) Tells us if the responder went unhandled, and
1710 // 2) prevents any NSBeep; we don't ever want to beep here.
1711 RetainPtr<WKResponderChainSink> sink = adoptNS([[WKResponderChainSink alloc] initWithResponderChain:m_view]);
1712 [m_view _web_superDoCommandBySelector:selector];
1714 return ![sink didReceiveUnhandledCommand];
1717 void WebViewImpl::executeEditCommandForSelector(SEL selector, const String& argument)
1719 m_page->executeEditCommand(commandNameForSelector(selector), argument);
1722 void WebViewImpl::registerEditCommand(RefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo)
1724 RefPtr<WebEditCommandProxy> command = prpCommand;
1726 RetainPtr<WKEditCommandObjC> commandObjC = adoptNS([[WKEditCommandObjC alloc] initWithWebEditCommandProxy:command]);
1727 String actionName = WebEditCommandProxy::nameForEditAction(command->editAction());
1729 NSUndoManager *undoManager = [m_view undoManager];
1730 [undoManager registerUndoWithTarget:m_undoTarget.get() selector:((undoOrRedo == WebPageProxy::Undo) ? @selector(undoEditing:) : @selector(redoEditing:)) object:commandObjC.get()];
1731 if (!actionName.isEmpty())
1732 [undoManager setActionName:(NSString *)actionName];
1735 void WebViewImpl::clearAllEditCommands()
1737 [[m_view undoManager] removeAllActionsWithTarget:m_undoTarget.get()];
1740 bool WebViewImpl::writeSelectionToPasteboard(NSPasteboard *pasteboard, NSArray *types)
1742 size_t numTypes = types.count;
1743 [pasteboard declareTypes:types owner:nil];
1744 for (size_t i = 0; i < numTypes; ++i) {
1745 if ([[types objectAtIndex:i] isEqualTo:NSStringPboardType])
1746 [pasteboard setString:m_page->stringSelectionForPasteboard() forType:NSStringPboardType];
1748 RefPtr<WebCore::SharedBuffer> buffer = m_page->dataSelectionForPasteboard([types objectAtIndex:i]);
1749 [pasteboard setData:buffer ? buffer->createNSData().get() : nil forType:[types objectAtIndex:i]];
1755 bool WebViewImpl::readSelectionFromPasteboard(NSPasteboard *pasteboard)
1757 return m_page->readSelectionFromPasteboard([pasteboard name]);
1760 id WebViewImpl::validRequestorForSendAndReturnTypes(NSString *sendType, NSString *returnType)
1762 EditorState editorState = m_page->editorState();
1763 bool isValidSendType = false;
1765 if (sendType && !editorState.selectionIsNone) {
1766 if (editorState.isInPlugin)
1767 isValidSendType = [sendType isEqualToString:NSStringPboardType];
1769 isValidSendType = [PasteboardTypes::forSelection() containsObject:sendType];
1772 bool isValidReturnType = false;
1774 isValidReturnType = true;
1775 else if ([PasteboardTypes::forEditing() containsObject:returnType] && editorState.isContentEditable) {
1776 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts.
1777 isValidReturnType = editorState.isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType];
1779 if (isValidSendType && isValidReturnType)
1781 return [[m_view nextResponder] validRequestorForSendType:sendType returnType:returnType];
1784 void WebViewImpl::centerSelectionInVisibleArea()
1786 m_page->centerSelectionInVisibleArea();
1789 void WebViewImpl::selectionDidChange()
1791 updateFontPanelIfNeeded();
1794 void WebViewImpl::startObservingFontPanel()
1796 [m_windowVisibilityObserver startObservingFontPanel];
1799 void WebViewImpl::updateFontPanelIfNeeded()
1801 const EditorState& editorState = m_page->editorState();
1802 if (editorState.selectionIsNone || !editorState.isContentEditable)
1804 if ([NSFontPanel sharedFontPanelExists] && [[NSFontPanel sharedFontPanel] isVisible]) {
1805 m_page->fontAtSelection([](const String& fontName, double fontSize, bool selectionHasMultipleFonts, WebKit::CallbackBase::Error error) {
1806 NSFont *font = [NSFont fontWithName:fontName size:fontSize];
1808 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:selectionHasMultipleFonts];
1813 void WebViewImpl::changeFontFromFontPanel()
1815 NSFontManager *fontManager = [NSFontManager sharedFontManager];
1816 NSFont *font = [fontManager convertFont:fontManager.selectedFont];
1819 m_page->setFont(font.familyName, font.pointSize, font.fontDescriptor.symbolicTraits);
1822 static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item)
1824 if (![(NSObject *)item isKindOfClass:[NSMenuItem class]])
1826 return (NSMenuItem *)item;
1829 static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item)
1831 if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]])
1833 return (NSToolbarItem *)item;
1836 bool WebViewImpl::validateUserInterfaceItem(id <NSValidatedUserInterfaceItem> item)
1838 SEL action = [item action];
1840 if (action == @selector(showGuessPanel:)) {
1841 if (NSMenuItem *menuItem = WebKit::menuItem(item))
1842 [menuItem setTitle:WebCore::contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])];
1843 return m_page->editorState().isContentEditable;
1846 if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:))
1847 return m_page->editorState().isContentEditable;
1849 if (action == @selector(toggleContinuousSpellChecking:)) {
1850 bool enabled = TextChecker::isContinuousSpellCheckingAllowed();
1851 bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled;
1852 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1856 if (action == @selector(toggleGrammarChecking:)) {
1857 bool checked = TextChecker::state().isGrammarCheckingEnabled;
1858 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1862 if (action == @selector(toggleAutomaticSpellingCorrection:)) {
1863 bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled;
1864 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1865 return m_page->editorState().isContentEditable;
1868 if (action == @selector(orderFrontSubstitutionsPanel:)) {
1869 if (NSMenuItem *menuItem = WebKit::menuItem(item))
1870 [menuItem setTitle:WebCore::contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])];
1871 return m_page->editorState().isContentEditable;
1874 if (action == @selector(toggleSmartInsertDelete:)) {
1875 bool checked = m_page->isSmartInsertDeleteEnabled();
1876 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1877 return m_page->editorState().isContentEditable;
1880 if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
1881 bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
1882 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1883 return m_page->editorState().isContentEditable;
1886 if (action == @selector(toggleAutomaticDashSubstitution:)) {
1887 bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled;
1888 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1889 return m_page->editorState().isContentEditable;
1892 if (action == @selector(toggleAutomaticLinkDetection:)) {
1893 bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled;
1894 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1895 return m_page->editorState().isContentEditable;
1898 if (action == @selector(toggleAutomaticTextReplacement:)) {
1899 bool checked = TextChecker::state().isAutomaticTextReplacementEnabled;
1900 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1901 return m_page->editorState().isContentEditable;
1904 if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:))
1905 return m_page->editorState().selectionIsRange && m_page->editorState().isContentEditable;
1907 if (action == @selector(stopSpeaking:))
1908 return [NSApp isSpeaking];
1910 // The centerSelectionInVisibleArea: selector is enabled if there's a selection range or if there's an insertion point in an editable area.
1911 if (action == @selector(centerSelectionInVisibleArea:))
1912 return m_page->editorState().selectionIsRange || (m_page->editorState().isContentEditable && !m_page->editorState().selectionIsNone);
1914 // Next, handle editor commands. Start by returning true for anything that is not an editor command.
1915 // Returning true is the default thing to do in an AppKit validate method for any selector that is not recognized.
1916 String commandName = commandNameForSelector([item action]);
1917 if (!WebCore::Editor::commandIsSupportedFromMenuOrKeyBinding(commandName))
1920 // Add this item to the vector of items for a given command that are awaiting validation.
1921 ValidationMap::AddResult addResult = m_validationMap.add(commandName, ValidationVector());
1922 addResult.iterator->value.append(item);
1923 if (addResult.isNewEntry) {
1924 // If we are not already awaiting validation for this command, start the asynchronous validation process.
1925 // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time
1926 // we asked for the same command; there is no guarantee the answer is still valid.
1927 auto weakThis = createWeakPtr();
1928 m_page->validateCommand(commandName, [weakThis](const String& commandName, bool isEnabled, int32_t state, WebKit::CallbackBase::Error error) {
1932 // If the process exits before the command can be validated, we'll be called back with an error.
1933 if (error != WebKit::CallbackBase::Error::None)
1936 weakThis->setUserInterfaceItemState(commandName, isEnabled, state);
1940 // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called.
1941 // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment.
1942 // But returning NO here would be worse; that would make keyboard commands such as command-C fail.
1946 void WebViewImpl::setUserInterfaceItemState(NSString *commandName, bool enabled, int state)
1948 ValidationVector items = m_validationMap.take(commandName);
1949 for (auto& item : items) {
1950 [menuItem(item.get()) setState:state];
1951 [menuItem(item.get()) setEnabled:enabled];
1952 [toolbarItem(item.get()) setEnabled:enabled];
1953 // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled.
1957 void WebViewImpl::startSpeaking()
1959 m_page->getSelectionOrContentsAsString([](const String& string, WebKit::CallbackBase::Error error) {
1960 if (error != WebKit::CallbackBase::Error::None)
1965 [NSApp speakString:string];
1969 void WebViewImpl::stopSpeaking(id sender)
1971 [NSApp stopSpeaking:sender];
1974 void WebViewImpl::showGuessPanel(id sender)
1976 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
1978 LOG_ERROR("No NSSpellChecker");
1982 NSPanel *spellingPanel = [checker spellingPanel];
1983 if ([spellingPanel isVisible]) {
1984 [spellingPanel orderOut:sender];
1988 m_page->advanceToNextMisspelling(true);
1989 [spellingPanel orderFront:sender];
1992 void WebViewImpl::checkSpelling()
1994 m_page->advanceToNextMisspelling(false);
1997 void WebViewImpl::changeSpelling(id sender)
1999 NSString *word = [[sender selectedCell] stringValue];
2001 m_page->changeSpellingToWord(word);
2004 void WebViewImpl::toggleContinuousSpellChecking()
2006 bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled;
2007 TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled);
2009 m_page->process().updateTextCheckerState();
2012 bool WebViewImpl::isGrammarCheckingEnabled()
2014 return TextChecker::state().isGrammarCheckingEnabled;
2017 void WebViewImpl::setGrammarCheckingEnabled(bool flag)
2019 if (static_cast<bool>(flag) == TextChecker::state().isGrammarCheckingEnabled)
2022 TextChecker::setGrammarCheckingEnabled(flag);
2023 m_page->process().updateTextCheckerState();
2026 void WebViewImpl::toggleGrammarChecking()
2028 bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled;
2029 TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled);
2031 m_page->process().updateTextCheckerState();
2034 void WebViewImpl::toggleAutomaticSpellingCorrection()
2036 TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled);
2038 m_page->process().updateTextCheckerState();
2041 void WebViewImpl::orderFrontSubstitutionsPanel(id sender)
2043 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2045 LOG_ERROR("No NSSpellChecker");
2049 NSPanel *substitutionsPanel = [checker substitutionsPanel];
2050 if ([substitutionsPanel isVisible]) {
2051 [substitutionsPanel orderOut:sender];
2054 [substitutionsPanel orderFront:sender];
2057 void WebViewImpl::toggleSmartInsertDelete()
2059 m_page->setSmartInsertDeleteEnabled(!m_page->isSmartInsertDeleteEnabled());
2062 bool WebViewImpl::isAutomaticQuoteSubstitutionEnabled()
2064 return TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
2067 void WebViewImpl::setAutomaticQuoteSubstitutionEnabled(bool flag)
2069 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled)
2072 TextChecker::setAutomaticQuoteSubstitutionEnabled(flag);
2073 m_page->process().updateTextCheckerState();
2076 void WebViewImpl::toggleAutomaticQuoteSubstitution()
2078 TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled);
2079 m_page->process().updateTextCheckerState();
2082 bool WebViewImpl::isAutomaticDashSubstitutionEnabled()
2084 return TextChecker::state().isAutomaticDashSubstitutionEnabled;
2087 void WebViewImpl::setAutomaticDashSubstitutionEnabled(bool flag)
2089 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled)
2092 TextChecker::setAutomaticDashSubstitutionEnabled(flag);
2093 m_page->process().updateTextCheckerState();
2096 void WebViewImpl::toggleAutomaticDashSubstitution()
2098 TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled);
2099 m_page->process().updateTextCheckerState();
2102 bool WebViewImpl::isAutomaticLinkDetectionEnabled()
2104 return TextChecker::state().isAutomaticLinkDetectionEnabled;
2107 void WebViewImpl::setAutomaticLinkDetectionEnabled(bool flag)
2109 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled)
2112 TextChecker::setAutomaticLinkDetectionEnabled(flag);
2113 m_page->process().updateTextCheckerState();
2116 void WebViewImpl::toggleAutomaticLinkDetection()
2118 TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled);
2119 m_page->process().updateTextCheckerState();
2122 bool WebViewImpl::isAutomaticTextReplacementEnabled()
2124 return TextChecker::state().isAutomaticTextReplacementEnabled;
2127 void WebViewImpl::setAutomaticTextReplacementEnabled(bool flag)
2129 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled)
2132 TextChecker::setAutomaticTextReplacementEnabled(flag);
2133 m_page->process().updateTextCheckerState();
2136 void WebViewImpl::toggleAutomaticTextReplacement()
2138 TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled);
2139 m_page->process().updateTextCheckerState();
2142 void WebViewImpl::uppercaseWord()
2144 m_page->uppercaseWord();
2147 void WebViewImpl::lowercaseWord()
2149 m_page->lowercaseWord();
2152 void WebViewImpl::capitalizeWord()
2154 m_page->capitalizeWord();
2157 void WebViewImpl::preferencesDidChange()
2159 BOOL needsViewFrameInWindowCoordinates = m_page->preferences().pluginsEnabled();
2161 if (!!needsViewFrameInWindowCoordinates == !!m_needsViewFrameInWindowCoordinates)
2164 m_needsViewFrameInWindowCoordinates = needsViewFrameInWindowCoordinates;
2166 updateWindowAndViewFrames();
2169 void WebViewImpl::setTextIndicator(WebCore::TextIndicator& textIndicator, WebCore::TextIndicatorWindowLifetime lifetime)
2171 if (!m_textIndicatorWindow)
2172 m_textIndicatorWindow = std::make_unique<WebCore::TextIndicatorWindow>(m_view);
2174 NSRect textBoundingRectInScreenCoordinates = [m_view.window convertRectToScreen:[m_view convertRect:textIndicator.textBoundingRectInRootViewCoordinates() toView:nil]];
2175 m_textIndicatorWindow->setTextIndicator(textIndicator, NSRectToCGRect(textBoundingRectInScreenCoordinates), lifetime);
2178 void WebViewImpl::clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation animation)
2180 if (m_textIndicatorWindow)
2181 m_textIndicatorWindow->clearTextIndicator(animation);
2182 m_textIndicatorWindow = nullptr;
2185 void WebViewImpl::setTextIndicatorAnimationProgress(float progress)
2187 if (m_textIndicatorWindow)
2188 m_textIndicatorWindow->setAnimationProgress(progress);
2191 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimation(bool animate)
2193 [m_view _web_dismissContentRelativeChildWindowsWithAnimation:animate];
2196 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimationFromViewOnly(bool animate)
2198 // Calling _clearTextIndicatorWithAnimation here will win out over the animated clear in dismissContentRelativeChildWindowsFromViewOnly.
2199 // We can't invert these because clients can override (and have overridden) _dismissContentRelativeChildWindows, so it needs to be called.
2200 // For this same reason, this can't be moved to WebViewImpl without care.
2201 clearTextIndicatorWithAnimation(animate ? WebCore::TextIndicatorWindowDismissalAnimation::FadeOut : WebCore::TextIndicatorWindowDismissalAnimation::None);
2202 [m_view _web_dismissContentRelativeChildWindows];
2205 void WebViewImpl::dismissContentRelativeChildWindowsFromViewOnly()
2207 bool hasActiveImmediateAction = false;
2208 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2209 hasActiveImmediateAction = [m_immediateActionController hasActiveImmediateAction];
2212 // FIXME: We don't know which panel we are dismissing, it may not even be in the current page (see <rdar://problem/13875766>).
2213 if (m_view.window.isKeyWindow || hasActiveImmediateAction) {
2214 WebCore::DictionaryLookup::hidePopup();
2216 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2217 DDActionsManager *actionsManager = [getDDActionsManagerClass() sharedManager];
2218 if ([actionsManager respondsToSelector:@selector(requestBubbleClosureUnanchorOnFailure:)])
2219 [actionsManager requestBubbleClosureUnanchorOnFailure:YES];
2223 clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::FadeOut);
2225 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2226 [m_immediateActionController dismissContentRelativeChildWindows];
2229 m_pageClient->dismissCorrectionPanel(WebCore::ReasonForDismissingAlternativeTextIgnored);
2232 void WebViewImpl::hideWordDefinitionWindow()
2234 WebCore::DictionaryLookup::hidePopup();
2237 void WebViewImpl::quickLookWithEvent(NSEvent *event)
2239 if (ignoresNonWheelEvents())
2242 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2243 if (m_immediateActionGestureRecognizer) {
2244 [m_view _web_superQuickLookWithEvent:event];
2249 NSPoint locationInViewCoordinates = [m_view convertPoint:[event locationInWindow] fromView:nil];
2250 m_page->performDictionaryLookupAtLocation(WebCore::FloatPoint(locationInViewCoordinates));
2253 void WebViewImpl::prepareForDictionaryLookup()
2255 if (m_didRegisterForLookupPopoverCloseNotifications)
2258 m_didRegisterForLookupPopoverCloseNotifications = true;
2260 [m_windowVisibilityObserver startObservingLookupDismissal];
2263 void WebViewImpl::setAllowsLinkPreview(bool allowsLinkPreview)
2265 if (m_allowsLinkPreview == allowsLinkPreview)
2268 m_allowsLinkPreview = allowsLinkPreview;
2270 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2271 if (!allowsLinkPreview)
2272 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
2273 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get())
2274 [m_view addGestureRecognizer:immediateActionRecognizer];
2278 void* WebViewImpl::immediateActionAnimationControllerForHitTestResult(API::HitTestResult* hitTestResult, uint32_t type, API::Object* userData)
2280 return [m_view _web_immediateActionAnimationControllerForHitTestResultInternal:hitTestResult withType:type userData:userData];
2283 void WebViewImpl::didPerformImmediateActionHitTest(const WebHitTestResultData& result, bool contentPreventsDefault, API::Object* userData)
2285 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2286 [m_immediateActionController didPerformImmediateActionHitTest:result contentPreventsDefault:contentPreventsDefault userData:userData];
2290 void WebViewImpl::prepareForImmediateActionAnimation()
2292 [m_view _web_prepareForImmediateActionAnimation];
2295 void WebViewImpl::cancelImmediateActionAnimation()
2297 [m_view _web_cancelImmediateActionAnimation];
2300 void WebViewImpl::completeImmediateActionAnimation()
2302 [m_view _web_completeImmediateActionAnimation];
2305 void WebViewImpl::didChangeContentSize(CGSize newSize)
2307 [m_view _web_didChangeContentSize:NSSizeFromCGSize(newSize)];
2310 void WebViewImpl::setIgnoresNonWheelEvents(bool ignoresNonWheelEvents)
2312 if (m_ignoresNonWheelEvents == ignoresNonWheelEvents)
2315 m_ignoresNonWheelEvents = ignoresNonWheelEvents;
2316 m_page->setShouldDispatchFakeMouseMoveEvents(!ignoresNonWheelEvents);
2318 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2319 if (ignoresNonWheelEvents)
2320 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
2321 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get()) {
2322 if (m_allowsLinkPreview)
2323 [m_view addGestureRecognizer:immediateActionRecognizer];
2328 void WebViewImpl::setIgnoresAllEvents(bool ignoresAllEvents)
2330 m_ignoresAllEvents = ignoresAllEvents;
2331 setIgnoresNonWheelEvents(ignoresAllEvents);
2334 void WebViewImpl::setIgnoresMouseDraggedEvents(bool ignoresMouseDraggedEvents)
2336 m_ignoresMouseDraggedEvents = ignoresMouseDraggedEvents;
2339 void WebViewImpl::setAccessibilityWebProcessToken(NSData *data)
2341 m_remoteAccessibilityChild = WKAXRemoteElementForToken(data);
2342 updateRemoteAccessibilityRegistration(true);
2345 void WebViewImpl::updateRemoteAccessibilityRegistration(bool registerProcess)
2347 // When the tree is connected/disconnected, the remote accessibility registration
2348 // needs to be updated with the pid of the remote process. If the process is going
2349 // away, that information is not present in WebProcess
2351 if (registerProcess)
2352 pid = m_page->process().processIdentifier();
2353 else if (!registerProcess) {
2354 pid = WKAXRemoteProcessIdentifier(m_remoteAccessibilityChild.get());
2355 m_remoteAccessibilityChild = nil;
2358 WKAXRegisterRemoteProcess(registerProcess, pid);
2361 void WebViewImpl::accessibilityRegisterUIProcessTokens()
2363 // Initialize remote accessibility when the window connection has been established.
2364 NSData *remoteElementToken = WKAXRemoteTokenForElement(m_view);
2365 NSData *remoteWindowToken = WKAXRemoteTokenForElement(m_view.window);
2366 IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
2367 IPC::DataReference windowToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]);
2368 m_page->registerUIProcessAccessibilityTokens(elementToken, windowToken);
2371 id WebViewImpl::accessibilityFocusedUIElement()
2373 enableAccessibilityIfNecessary();
2374 return m_remoteAccessibilityChild.get();
2377 id WebViewImpl::accessibilityHitTest(CGPoint)
2379 return accessibilityFocusedUIElement();
2382 void WebViewImpl::enableAccessibilityIfNecessary()
2384 if (WebCore::AXObjectCache::accessibilityEnabled())
2387 // After enabling accessibility update the window frame on the web process so that the
2388 // correct accessibility position is transmitted (when AX is off, that position is not calculated).
2389 WebCore::AXObjectCache::enableAccessibility();
2390 updateWindowAndViewFrames();
2393 id WebViewImpl::accessibilityAttributeValue(NSString *attribute)
2395 enableAccessibilityIfNecessary();
2397 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
2400 if (m_remoteAccessibilityChild)
2401 child = m_remoteAccessibilityChild.get();
2405 return [NSArray arrayWithObject:child];
2407 if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
2408 return NSAccessibilityGroupRole;
2409 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
2410 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
2411 if ([attribute isEqualToString:NSAccessibilityParentAttribute])
2412 return NSAccessibilityUnignoredAncestor([m_view superview]);
2413 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
2416 return [m_view _web_superAccessibilityAttributeValue:attribute];
2419 void WebViewImpl::setPrimaryTrackingArea(NSTrackingArea *trackingArea)
2421 [m_view removeTrackingArea:m_primaryTrackingArea.get()];
2422 m_primaryTrackingArea = trackingArea;
2423 [m_view addTrackingArea:trackingArea];
2426 // Any non-zero value will do, but using something recognizable might help us debug some day.
2427 #define TRACKING_RECT_TAG 0xBADFACE
2429 NSTrackingRectTag WebViewImpl::addTrackingRect(CGRect, id owner, void* userData, bool assumeInside)
2431 ASSERT(m_trackingRectOwner == nil);
2432 m_trackingRectOwner = owner;
2433 m_trackingRectUserData = userData;
2434 return TRACKING_RECT_TAG;
2437 NSTrackingRectTag WebViewImpl::addTrackingRectWithTrackingNum(CGRect, id owner, void* userData, bool assumeInside, int tag)
2439 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
2440 ASSERT(m_trackingRectOwner == nil);
2441 m_trackingRectOwner = owner;
2442 m_trackingRectUserData = userData;
2443 return TRACKING_RECT_TAG;
2446 void WebViewImpl::addTrackingRectsWithTrackingNums(CGRect*, id owner, void** userDataList, bool assumeInside, NSTrackingRectTag *trackingNums, int count)
2449 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
2450 ASSERT(m_trackingRectOwner == nil);
2451 m_trackingRectOwner = owner;
2452 m_trackingRectUserData = userDataList[0];
2453 trackingNums[0] = TRACKING_RECT_TAG;
2456 void WebViewImpl::removeTrackingRect(NSTrackingRectTag tag)
2461 if (tag == TRACKING_RECT_TAG) {
2462 m_trackingRectOwner = nil;
2466 if (tag == m_lastToolTipTag) {
2467 [m_view _web_superRemoveTrackingRect:tag];
2468 m_lastToolTipTag = 0;
2472 // If any other tracking rect is being removed, we don't know how it was created
2473 // and it's possible there's a leak involved (see 3500217)
2474 ASSERT_NOT_REACHED();
2477 void WebViewImpl::removeTrackingRects(NSTrackingRectTag *tags, int count)
2479 for (int i = 0; i < count; ++i) {
2483 ASSERT(tag == TRACKING_RECT_TAG);
2484 m_trackingRectOwner = nil;
2488 void WebViewImpl::sendToolTipMouseExited()
2490 // Nothing matters except window, trackingNumber, and userData.
2491 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
2492 location:NSMakePoint(0, 0)
2495 windowNumber:m_view.window.windowNumber
2498 trackingNumber:TRACKING_RECT_TAG
2499 userData:m_trackingRectUserData];
2500 [m_trackingRectOwner mouseExited:fakeEvent];
2503 void WebViewImpl::sendToolTipMouseEntered()
2505 // Nothing matters except window, trackingNumber, and userData.
2506 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
2507 location:NSMakePoint(0, 0)
2510 windowNumber:m_view.window.windowNumber
2513 trackingNumber:TRACKING_RECT_TAG
2514 userData:m_trackingRectUserData];
2515 [m_trackingRectOwner mouseEntered:fakeEvent];
2518 NSString *WebViewImpl::stringForToolTip(NSToolTipTag tag)
2520 return nsStringFromWebCoreString(m_page->toolTip());
2523 void WebViewImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip)
2525 if (!oldToolTip.isNull())
2526 sendToolTipMouseExited();
2528 if (!newToolTip.isEmpty()) {
2529 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
2530 [m_view removeAllToolTips];
2531 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
2532 m_lastToolTipTag = [m_view addToolTipRect:wideOpenRect owner:m_view userData:NULL];
2533 sendToolTipMouseEntered();
2537 void WebViewImpl::setAcceleratedCompositingRootLayer(CALayer *rootLayer)
2539 [rootLayer web_disableAllActions];
2541 m_rootLayer = rootLayer;
2544 if (m_thumbnailView) {
2545 updateThumbnailViewLayer();
2550 [CATransaction begin];
2551 [CATransaction setDisableActions:YES];
2554 if (!m_layerHostingView) {
2555 // Create an NSView that will host our layer tree.
2556 m_layerHostingView = adoptNS([[WKFlippedView alloc] initWithFrame:m_view.bounds]);
2557 [m_layerHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
2559 [m_view addSubview:m_layerHostingView.get() positioned:NSWindowBelow relativeTo:nil];
2561 // Create a root layer that will back the NSView.
2562 RetainPtr<CALayer> layer = adoptNS([[CALayer alloc] init]);
2563 [layer setDelegate:[WebActionDisablingCALayerDelegate shared]];
2565 [layer setName:@"Hosting root layer"];
2568 [m_layerHostingView setLayer:layer.get()];
2569 [m_layerHostingView setWantsLayer:YES];
2572 [m_layerHostingView layer].sublayers = [NSArray arrayWithObject:rootLayer];
2573 } else if (m_layerHostingView) {
2574 [m_layerHostingView removeFromSuperview];
2575 [m_layerHostingView setLayer:nil];
2576 [m_layerHostingView setWantsLayer:NO];
2578 m_layerHostingView = nullptr;
2581 [CATransaction commit];
2585 void WebViewImpl::setThumbnailView(_WKThumbnailView *thumbnailView)
2587 ASSERT(!m_thumbnailView || !thumbnailView);
2589 m_thumbnailView = thumbnailView;
2592 updateThumbnailViewLayer();
2594 setAcceleratedCompositingRootLayer(m_rootLayer.get());
2597 void WebViewImpl::reparentLayerTreeInThumbnailView()
2599 m_thumbnailView._thumbnailLayer = m_rootLayer.get();
2602 void WebViewImpl::updateThumbnailViewLayer()
2604 _WKThumbnailView *thumbnailView = m_thumbnailView;
2605 ASSERT(thumbnailView);
2607 if (thumbnailView._waitingForSnapshot && m_view.window)
2608 reparentLayerTreeInThumbnailView();
2611 void WebViewImpl::setInspectorAttachmentView(NSView *newView)
2613 NSView *oldView = m_inspectorAttachmentView.get();
2614 if (oldView == newView)
2617 m_inspectorAttachmentView = newView;
2618 m_page->inspector()->attachmentViewDidChange(oldView ? oldView : m_view, newView ? newView : m_view);
2621 NSView *WebViewImpl::inspectorAttachmentView()
2623 NSView *attachmentView = m_inspectorAttachmentView.get();
2624 return attachmentView ? attachmentView : m_view;
2627 _WKRemoteObjectRegistry *WebViewImpl::remoteObjectRegistry()
2629 if (!m_remoteObjectRegistry) {
2630 m_remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:m_page]);
2631 m_page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID(), [m_remoteObjectRegistry remoteObjectRegistry]);
2634 return m_remoteObjectRegistry.get();
2637 WKBrowsingContextController *WebViewImpl::browsingContextController()
2639 if (!m_browsingContextController)
2640 m_browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(m_page.ptr())]);
2642 return m_browsingContextController.get();
2644 #endif // WK_API_ENABLED
2646 #if ENABLE(DRAG_SUPPORT)
2647 void WebViewImpl::draggedImage(NSImage *image, CGPoint endPoint, NSDragOperation operation)
2649 #pragma clang diagnostic push
2650 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2651 NSPoint windowImageLoc = [m_view.window convertScreenToBase:NSPointFromCGPoint(endPoint)];
2652 #pragma clang diagnostic pop
2653 NSPoint windowMouseLoc = windowImageLoc;
2655 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
2656 m_ignoresMouseDraggedEvents = true;
2658 m_page->dragEnded(WebCore::IntPoint(windowMouseLoc), WebCore::globalPoint(windowMouseLoc, m_view.window), operation);
2661 static WebCore::DragApplicationFlags applicationFlagsForDrag(NSView *view, id <NSDraggingInfo> draggingInfo)
2664 if ([NSApp modalWindow])
2665 flags = WebCore::DragApplicationIsModal;
2666 if (view.window.attachedSheet)
2667 flags |= WebCore::DragApplicationHasAttachedSheet;
2668 if (draggingInfo.draggingSource == view)
2669 flags |= WebCore::DragApplicationIsSource;
2670 if ([NSApp currentEvent].modifierFlags & NSAlternateKeyMask)
2671 flags |= WebCore::DragApplicationIsCopyKeyDown;
2672 return static_cast<WebCore::DragApplicationFlags>(flags);
2676 NSDragOperation WebViewImpl::draggingEntered(id <NSDraggingInfo> draggingInfo)
2678 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2679 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2680 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2682 m_page->resetCurrentDragInformation();
2683 m_page->dragEntered(dragData, draggingInfo.draggingPasteboard.name);
2684 return NSDragOperationCopy;
2687 NSDragOperation WebViewImpl::draggingUpdated(id <NSDraggingInfo> draggingInfo)
2689 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2690 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2691 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2692 m_page->dragUpdated(dragData, draggingInfo.draggingPasteboard.name);
2694 NSInteger numberOfValidItemsForDrop = m_page->currentDragNumberOfFilesToBeAccepted();
2695 NSDraggingFormation draggingFormation = NSDraggingFormationNone;
2696 if (m_page->currentDragIsOverFileInput() && numberOfValidItemsForDrop > 0)
2697 draggingFormation = NSDraggingFormationList;
2699 if (draggingInfo.numberOfValidItemsForDrop != numberOfValidItemsForDrop)
2700 [draggingInfo setNumberOfValidItemsForDrop:numberOfValidItemsForDrop];
2701 if (draggingInfo.draggingFormation != draggingFormation)
2702 [draggingInfo setDraggingFormation:draggingFormation];
2704 return m_page->currentDragOperation();
2707 void WebViewImpl::draggingExited(id <NSDraggingInfo> draggingInfo)
2709 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2710 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2711 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2712 m_page->dragExited(dragData, draggingInfo.draggingPasteboard.name);
2713 m_page->resetCurrentDragInformation();
2716 bool WebViewImpl::prepareForDragOperation(id <NSDraggingInfo>)
2721 // FIXME: This code is more or less copied from Pasteboard::getBestURL.
2722 // It would be nice to be able to share the code somehow.
2723 static bool maybeCreateSandboxExtensionFromPasteboard(NSPasteboard *pasteboard, SandboxExtension::Handle& sandboxExtensionHandle)
2725 NSArray *types = pasteboard.types;
2726 if (![types containsObject:NSFilenamesPboardType])
2729 NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
2730 if (files.count != 1)
2733 NSString *file = [files objectAtIndex:0];
2735 if (![[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory])
2741 SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle);
2745 static void createSandboxExtensionsForFileUpload(NSPasteboard *pasteboard, SandboxExtension::HandleArray& handles)
2747 NSArray *types = pasteboard.types;
2748 if (![types containsObject:NSFilenamesPboardType])
2751 NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
2752 handles.allocate(files.count);
2753 for (unsigned i = 0; i < files.count; i++) {
2754 NSString *file = [files objectAtIndex:i];
2755 if (![[NSFileManager defaultManager] fileExistsAtPath:file])
2757 SandboxExtension::Handle handle;
2758 SandboxExtension::createHandle(file, SandboxExtension::ReadOnly, handles[i]);
2762 bool WebViewImpl::performDragOperation(id <NSDraggingInfo> draggingInfo)
2764 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2765 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2766 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2768 SandboxExtension::Handle sandboxExtensionHandle;
2769 bool createdExtension = maybeCreateSandboxExtensionFromPasteboard(draggingInfo.draggingPasteboard, sandboxExtensionHandle);
2770 if (createdExtension)
2771 m_page->process().willAcquireUniversalFileReadSandboxExtension();
2773 SandboxExtension::HandleArray sandboxExtensionForUpload;
2774 createSandboxExtensionsForFileUpload(draggingInfo.draggingPasteboard, sandboxExtensionForUpload);
2776 m_page->performDragOperation(dragData, draggingInfo.draggingPasteboard.name, sandboxExtensionHandle, sandboxExtensionForUpload);
2781 NSView *WebViewImpl::hitTestForDragTypes(CGPoint point, NSSet *types)
2783 // This code is needed to support drag and drop when the drag types cannot be matched.
2784 // This is the case for elements that do not place content
2785 // in the drag pasteboard automatically when the drag start (i.e. dragging a DIV element).
2786 if ([[m_view superview] mouse:NSPointFromCGPoint(point) inRect:[m_view frame]])
2791 void WebViewImpl::registerDraggedTypes()
2793 auto types = adoptNS([[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()]);
2794 [types addObjectsFromArray:PasteboardTypes::forURL()];
2795 [m_view registerForDraggedTypes:[types allObjects]];
2797 #endif // ENABLE(DRAG_SUPPORT)
2799 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
2800 void WebViewImpl::startWindowDrag()
2802 [m_view.window performWindowDragWithEvent:m_lastMouseDownEvent.get()];
2806 void WebViewImpl::dragImageForView(NSView *view, NSImage *image, CGPoint clientPoint, bool linkDrag)
2808 // The call below could release the view.
2809 RetainPtr<NSView> protector(m_view);
2811 #pragma clang diagnostic push
2812 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2813 [view dragImage:image
2814 at:NSPointFromCGPoint(clientPoint)
2816 event:linkDrag ? [NSApp currentEvent] : m_lastMouseDownEvent.get()
2817 pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
2820 #pragma clang diagnostic pop
2823 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
2825 NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
2826 return hasCaseInsensitiveSuffix(filename, extensionAsSuffix) || (stringIsCaseInsensitiveEqualToString(extension, @"jpeg")
2827 && hasCaseInsensitiveSuffix(filename, @".jpg"));
2830 void WebViewImpl::setFileAndURLTypes(NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, NSPasteboard *pasteboard)
2832 if (!matchesExtensionOrEquivalent(filename, extension))
2833 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:extension];
2835 [pasteboard setString:visibleURL forType:NSStringPboardType];
2836 [pasteboard setString:visibleURL forType:PasteboardTypes::WebURLPboardType];
2837 [pasteboard setString:title forType:PasteboardTypes::WebURLNamePboardType];
2838 [pasteboard setPropertyList:[NSArray arrayWithObjects:[NSArray arrayWithObject:visibleURL], [NSArray arrayWithObject:title], nil] forType:PasteboardTypes::WebURLsWithTitlesPboardType];
2839 [pasteboard setPropertyList:[NSArray arrayWithObject:extension] forType:NSFilesPromisePboardType];
2840 m_promisedFilename = filename;
2841 m_promisedURL = url;
2844 void WebViewImpl::setPromisedDataForImage(WebCore::Image* image, NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, WebCore::SharedBuffer* archiveBuffer, NSString *pasteboardName)
2846 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
2847 RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
2849 [types addObjectsFromArray:archiveBuffer ? PasteboardTypes::forImagesWithArchive() : PasteboardTypes::forImages()];
2850 [pasteboard declareTypes:types.get() owner:m_view];
2851 setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
2854 [pasteboard setData:archiveBuffer->createNSData().get() forType:PasteboardTypes::WebArchivePboardType];
2856 m_promisedImage = image;
2859 #if ENABLE(ATTACHMENT_ELEMENT)
2860 void WebViewImpl::setPromisedDataForAttachment(NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, NSString *pasteboardName)
2862 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
2863 RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
2864 [types addObjectsFromArray:PasteboardTypes::forURL()];
2865 [pasteboard declareTypes:types.get() owner:m_view];
2866 setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
2868 RetainPtr<NSMutableArray> paths = adoptNS([[NSMutableArray alloc] init]);
2869 [paths addObject:title];
2870 [pasteboard setPropertyList:paths.get() forType:NSFilenamesPboardType];
2872 m_promisedImage = nullptr;
2876 void WebViewImpl::pasteboardChangedOwner(NSPasteboard *pasteboard)
2878 m_promisedImage = nullptr;
2879 m_promisedFilename = "";
2883 void WebViewImpl::provideDataForPasteboard(NSPasteboard *pasteboard, NSString *type)
2885 // FIXME: need to support NSRTFDPboardType
2887 if ([type isEqual:NSTIFFPboardType] && m_promisedImage) {
2888 [pasteboard setData:(NSData *)m_promisedImage->getTIFFRepresentation() forType:NSTIFFPboardType];
2889 m_promisedImage = nullptr;
2893 static BOOL fileExists(NSString *path)
2895 struct stat statBuffer;
2896 return !lstat([path fileSystemRepresentation], &statBuffer);
2899 static NSString *pathWithUniqueFilenameForPath(NSString *path)
2901 // "Fix" the filename of the path.
2902 NSString *filename = filenameByFixingIllegalCharacters([path lastPathComponent]);
2903 path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename];
2905 if (fileExists(path)) {
2906 // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename.
2907 NSString *extensions = nil;
2908 NSString *pathWithoutExtensions;
2909 NSString *lastPathComponent = [path lastPathComponent];
2910 NSRange periodRange = [lastPathComponent rangeOfString:@"."];
2912 if (periodRange.location == NSNotFound) {
2913 pathWithoutExtensions = path;
2915 extensions = [lastPathComponent substringFromIndex:periodRange.location + 1];
2916 lastPathComponent = [lastPathComponent substringToIndex:periodRange.location];
2917 pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent];
2920 for (unsigned i = 1; ; i++) {
2921 NSString *pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i];
2922 path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber;
2923 if (!fileExists(path))
2931 NSArray *WebViewImpl::namesOfPromisedFilesDroppedAtDestination(NSURL *dropDestination)
2933 RetainPtr<NSFileWrapper> wrapper;
2934 RetainPtr<NSData> data;
2936 if (m_promisedImage) {
2937 data = m_promisedImage->data()->createNSData();
2938 wrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data.get()]);
2940 wrapper = adoptNS([[NSFileWrapper alloc] initWithURL:[NSURL URLWithString:m_promisedURL] options:NSFileWrapperReadingImmediate error:nil]);
2943 [wrapper setPreferredFilename:m_promisedFilename];
2945 LOG_ERROR("Failed to create image file.");
2949 // FIXME: Report an error if we fail to create a file.
2950 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
2951 path = pathWithUniqueFilenameForPath(path);
2952 if (![wrapper writeToURL:[NSURL fileURLWithPath:path] options:NSFileWrapperWritingWithNameUpdating originalContentsURL:nil error:nullptr])
2953 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToURL:options:originalContentsURL:error:]");
2955 if (!m_promisedURL.isEmpty())
2956 WebCore::setMetadataURL(m_promisedURL, "", String(path));
2958 return [NSArray arrayWithObject:[path lastPathComponent]];
2961 static RetainPtr<CGImageRef> takeWindowSnapshot(CGSWindowID windowID, bool captureAtNominalResolution)
2963 CGSWindowCaptureOptions options = kCGSCaptureIgnoreGlobalClipShape;
2964 if (captureAtNominalResolution)
2965 options |= kCGSWindowCaptureNominalResolution;
2966 RetainPtr<CFArrayRef> windowSnapshotImages = adoptCF(CGSHWCaptureWindowList(CGSMainConnectionID(), &windowID, 1, options));
2968 if (windowSnapshotImages && CFArrayGetCount(windowSnapshotImages.get()))
2969 return (CGImageRef)CFArrayGetValueAtIndex(windowSnapshotImages.get(), 0);
2971 // Fall back to the non-hardware capture path if we didn't get a snapshot
2972 // (which usually happens if the window is fully off-screen).
2973 CGWindowImageOption imageOptions = kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque;
2974 if (captureAtNominalResolution)
2975 imageOptions |= kCGWindowImageNominalResolution;
2976 return adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, imageOptions));
2979 RefPtr<ViewSnapshot> WebViewImpl::takeViewSnapshot()
2981 NSWindow *window = m_view.window;
2983 CGSWindowID windowID = (CGSWindowID)window.windowNumber;
2984 if (!windowID || !window.isVisible)
2987 RetainPtr<CGImageRef> windowSnapshotImage = takeWindowSnapshot(windowID, false);
2988 if (!windowSnapshotImage)
2991 // Work around <rdar://problem/17084993>; re-request the snapshot at kCGWindowImageNominalResolution if it was captured at the wrong scale.
2992 CGFloat desiredSnapshotWidth = window.frame.size.width * window.screen.backingScaleFactor;
2993 if (CGImageGetWidth(windowSnapshotImage.get()) != desiredSnapshotWidth)
2994 windowSnapshotImage = takeWindowSnapshot(windowID, true);
2996 if (!windowSnapshotImage)
2999 ViewGestureController& gestureController = ensureGestureController();
3001 NSRect windowCaptureRect;
3002 WebCore::FloatRect boundsForCustomSwipeViews = gestureController.windowRelativeBoundsForCustomSwipeViews();
3003 if (!boundsForCustomSwipeViews.isEmpty())
3004 windowCaptureRect = boundsForCustomSwipeViews;
3006 NSRect unobscuredBounds = m_view.bounds;
3007 float topContentInset = m_page->topContentInset();
3008 unobscuredBounds.origin.y += topContentInset;
3009 unobscuredBounds.size.height -= topContentInset;
3010 windowCaptureRect = [m_view convertRect:unobscuredBounds toView:nil];
3013 NSRect windowCaptureScreenRect = [window convertRectToScreen:windowCaptureRect];
3014 CGRect windowScreenRect;
3015 CGSGetScreenRectForWindow(CGSMainConnectionID(), (CGSWindowID)[window windowNumber], &windowScreenRect);
3017 NSRect croppedImageRect = windowCaptureRect;
3018 croppedImageRect.origin.y = windowScreenRect.size.height - windowCaptureScreenRect.size.height - NSMinY(windowCaptureRect);
3020 auto croppedSnapshotImage = adoptCF(CGImageCreateWithImageInRect(windowSnapshotImage.get(), NSRectToCGRect([window convertRectToBacking:croppedImageRect])));
3022 auto surface = WebCore::IOSurface::createFromImage(croppedSnapshotImage.get());
3025 surface->setIsVolatile(true);
3027 return ViewSnapshot::create(WTF::move(surface));
3030 void WebViewImpl::saveBackForwardSnapshotForCurrentItem()
3032 m_page->recordNavigationSnapshot();
3035 void WebViewImpl::saveBackForwardSnapshotForItem(WebBackForwardListItem& item)
3037 m_page->recordNavigationSnapshot(item);
3040 ViewGestureController& WebViewImpl::ensureGestureController()
3042 if (!m_gestureController)
3043 m_gestureController = std::make_unique<ViewGestureController>(m_page);
3044 return *m_gestureController;
3047 void WebViewImpl::setAllowsBackForwardNavigationGestures(bool allowsBackForwardNavigationGestures)
3049 m_allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
3050 m_page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
3051 m_page->setShouldUseImplicitRubberBandControl(allowsBackForwardNavigationGestures);
3054 void WebViewImpl::setAllowsMagnification(bool allowsMagnification)
3056 m_allowsMagnification = allowsMagnification;
3059 void WebViewImpl::setMagnification(double magnification, CGPoint centerPoint)
3061 if (magnification <= 0 || isnan(magnification) || isinf(magnification))
3062 [NSException raise:NSInvalidArgumentException format:@"Magnification should be a positive number"];
3064 dismissContentRelativeChildWindowsWithAnimation(false);
3066 m_page->scalePageInViewCoordinates(magnification, WebCore::roundedIntPoint(centerPoint));
3069 void WebViewImpl::setMagnification(double magnification)
3071 if (magnification <= 0 || isnan(magnification) || isinf(magnification))
3072 [NSException raise:NSInvalidArgumentException format:@"Magnification should be a positive number"];
3074 dismissContentRelativeChildWindowsWithAnimation(false);
3076 WebCore::FloatPoint viewCenter(NSMidX([m_view bounds]), NSMidY([m_view bounds]));
3077 m_page->scalePageInViewCoordinates(magnification, roundedIntPoint(viewCenter));
3080 double WebViewImpl::magnification() const
3082 if (m_gestureController)
3083 return m_gestureController->magnification();
3084 return m_page->pageScaleFactor();
3087 void WebViewImpl::setCustomSwipeViews(NSArray *customSwipeViews)
3089 if (!customSwipeViews.count && !m_gestureController)
3092 Vector<RetainPtr<NSView>> views;
3093 views.reserveInitialCapacity(customSwipeViews.count);
3094 for (NSView *view in customSwipeViews)
3095 views.uncheckedAppend(view);
3097 ensureGestureController().setCustomSwipeViews(views);
3100 void WebViewImpl::setCustomSwipeViewsTopContentInset(float topContentInset)
3102 ensureGestureController().setCustomSwipeViewsTopContentInset(topContentInset);
3105 bool WebViewImpl::tryToSwipeWithEvent(NSEvent *event, bool ignoringPinnedState)
3107 if (!m_allowsBackForwardNavigationGestures)
3110 auto& gestureController = ensureGestureController();
3112 bool wasIgnoringPinnedState = gestureController.shouldIgnorePinnedState();
3113 gestureController.setShouldIgnorePinnedState(ignoringPinnedState);
3115 bool handledEvent = gestureController.handleScrollWheelEvent(event);
3117 gestureController.setShouldIgnorePinnedState(wasIgnoringPinnedState);
3119 return handledEvent;
3122 void WebViewImpl::setDidMoveSwipeSnapshotCallback(void(^callback)(CGRect))
3124 if (!m_allowsBackForwardNavigationGestures)
3127 ensureGestureController().setDidMoveSwipeSnapshotCallback(callback);
3130 void WebViewImpl::scrollWheel(NSEvent *event)
3132 if (m_ignoresAllEvents)
3135 if (event.phase == NSEventPhaseBegan)
3136 dismissContentRelativeChildWindowsWithAnimation(false);
3138 if (m_allowsBackForwardNavigationGestures && ensureGestureController().handleScrollWheelEvent(event))
3141 NativeWebWheelEvent webEvent = NativeWebWheelEvent(event, m_view);
3142 m_page->handleWheelEvent(webEvent);
3145 void WebViewImpl::swipeWithEvent(NSEvent *event)
3147 if (m_ignoresNonWheelEvents)
3150 if (!m_allowsBackForwardNavigationGestures) {
3151 [m_view _web_superSwipeWithEvent:event];
3155 if (event.deltaX > 0.0)
3157 else if (event.deltaX < 0.0)
3158 m_page->goForward();
3160 [m_view _web_superSwipeWithEvent:event];
3163 void WebViewImpl::magnifyWithEvent(NSEvent *event)
3165 if (!m_allowsMagnification) {
3166 #if ENABLE(MAC_GESTURE_EVENTS)
3167 NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
3168 m_page->handleGestureEvent(webEvent);
3170 [m_view _web_superMagnifyWithEvent:event];
3174 dismissContentRelativeChildWindowsWithAnimation(false);
3176 auto& gestureController = ensureGestureController();
3178 #if ENABLE(MAC_GESTURE_EVENTS)
3179 if (gestureController.hasActiveMagnificationGesture()) {
3180 gestureController.handleMagnificationGestureEvent(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
3184 NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
3185 m_page->handleGestureEvent(webEvent);
3187 gestureController.handleMagnificationGestureEvent(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
3191 void WebViewImpl::smartMagnifyWithEvent(NSEvent *event)
3193 if (!m_allowsMagnification) {
3194 [m_view _web_superSmartMagnifyWithEvent:event];
3198 dismissContentRelativeChildWindowsWithAnimation(false);
3200 ensureGestureController().handleSmartMagnificationGesture([m_view convertPoint:event.locationInWindow fromView:nil]);
3203 void WebViewImpl::setLastMouseDownEvent(NSEvent *event)
3205 ASSERT(!event || event.type == NSLeftMouseDown || event.type == NSRightMouseDown || event.type == NSOtherMouseDown);
3207 if (event == m_lastMouseDownEvent.get())
3210 m_lastMouseDownEvent = event;
3213 #if ENABLE(MAC_GESTURE_EVENTS)
3214 void WebViewImpl::rotateWithEvent(NSEvent *event)
3216 NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
3217 m_page->handleGestureEvent(webEvent);
3221 void WebViewImpl::gestureEventWasNotHandledByWebCore(NSEvent *event)
3223 [m_view _web_gestureEventWasNotHandledByWebCore:event];
3226 void WebViewImpl::gestureEventWasNotHandledByWebCoreFromViewOnly(NSEvent *event)
3228 #if ENABLE(MAC_GESTURE_EVENTS)
3229 if (m_gestureController)
3230 m_gestureController->gestureEventWasNotHandledByWebCore(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
3234 void WebViewImpl::doneWithKeyEvent(NSEvent *event, bool eventWasHandled)
3236 if ([event type] != NSKeyDown)
3239 if (tryPostProcessPluginComplexTextInputKeyDown(event))
3242 if (eventWasHandled) {
3243 [NSCursor setHiddenUntilMouseMoves:YES];
3247 // resending the event may destroy this WKView
3248 RetainPtr<NSView> protector(m_view);
3250 ASSERT(!m_keyDownEventBeingResent);
3251 m_keyDownEventBeingResent = event;
3252 [NSApp _setCurrentEvent:event];
3253 [NSApp sendEvent:event];
3255 m_keyDownEventBeingResent = nullptr;
3258 NSArray *WebViewImpl::validAttributesForMarkedText()
3260 static NSArray *validAttributes;
3261 if (!validAttributes) {
3262 validAttributes = @[ NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, NSMarkedClauseSegmentAttributeName,
3263 #if USE(DICTATION_ALTERNATIVES)
3264 NSTextAlternativesAttributeName,
3266 #if USE(INSERTION_UNDO_GROUPING)
3267 NSTextInsertionUndoableAttributeName,
3270 // NSText also supports the following attributes, but it's
3271 // hard to tell which are really required for text input to
3272 // work well; I have not seen any input method make use of them yet.
3273 // NSFontAttributeName, NSForegroundColorAttributeName,
3274 // NSBackgroundColorAttributeName, NSLanguageAttributeName.
3275 CFRetain(validAttributes);
3277 LOG(TextInput, "validAttributesForMarkedText -> (...)");
3278 return validAttributes;
3281 static Vector<WebCore::CompositionUnderline> extractUnderlines(NSAttributedString *string)
3283 Vector<WebCore::CompositionUnderline> result;
3284 int length = string.string.length;
3286 for (int i = 0; i < length;) {
3288 NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
3290 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
3291 WebCore::Color color = WebCore::Color::black;
3292 if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
3293 color = WebCore::colorFromNSColor(colorAttr);
3294 result.append(WebCore::CompositionUnderline(range.location, NSMaxRange(range), color, style.intValue > 1));
3297 i = range.location + range.length;
3303 static bool eventKeyCodeIsZeroOrNumLockOrFn(NSEvent *event)
3305 unsigned short keyCode = [event keyCode];
3306 return !keyCode || keyCode == 10 || keyCode == 63;
3309 #if USE(ASYNC_NSTEXTINPUTCLIENT)
3311 Vector<WebCore::KeypressCommand> WebViewImpl::collectKeyboardLayoutCommandsForEvent(NSEvent *event)
3313 Vector<WebCore::KeypressCommand> commands;
3315 if ([event type] != NSKeyDown)
3318 ASSERT(!m_collectedKeypressCommands);
3319 m_collectedKeypressCommands = &commands;
3321 if (NSTextInputContext *context = inputContext())
3322 [context handleEventByKeyboardLayout:event];
3324 [m_view interpretKeyEvents:[NSArray arrayWithObject:event]];
3326 m_collectedKeypressCommands = nullptr;
3331 void WebViewImpl::interpretKeyEvent(NSEvent *event, void(^completionHandler)(BOOL handled, const Vector<WebCore::KeypressCommand>& commands))
3333 // For regular Web content, input methods run before passing a keydown to DOM, but plug-ins get an opportunity to handle the event first.
3334 // There is no need to collect commands, as the plug-in cannot execute them.
3335 if (pluginComplexTextInputIdentifier()) {
3336 completionHandler(NO, { });
3340 if (!inputContext()) {
3341 auto commands = collectKeyboardLayoutCommandsForEvent(event);
3342 completionHandler(NO, commands);
3346 LOG(TextInput, "-> handleEventByInputMethod:%p %@", event, event);
3347 [inputContext() handleEventByInputMethod:event completionHandler:^(BOOL handled) {
3349 LOG(TextInput, "... handleEventByInputMethod%s handled", handled ? "" : " not");
3351 completionHandler(YES, { });
3355 auto commands = collectKeyboardLayoutCommandsForEvent(event);
3356 completionHandler(NO, commands);
3360 void WebViewImpl::doCommandBySelector(SEL selector)
3362 LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
3364 if (auto* keypressCommands = m_collectedKeypressCommands) {
3365 WebCore::KeypressCommand command(NSStringFromSelector(selector));
3366 keypressCommands->append(command);
3367 LOG(TextInput, "...stored");
3368 m_page->registerKeypressCommandName(command.commandName);
3370 // FIXME: Send the command to Editor synchronously and only send it along the
3371 // responder chain if it's a selector that does not correspond to an editing command.
3372 [m_view _web_superDoCommandBySelector:selector];
3376 void WebViewImpl::insertText(id string)
3378 // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
3379 // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
3380 // command ensures that a keypress event is dispatched as appropriate.
3381 insertText(string, NSMakeRange(NSNotFound, 0));
3384 void WebViewImpl::insertText(id string, NSRange replacementRange)
3386 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3387 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
3389 if (replacementRange.location != NSNotFound)
3390 LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
3392 LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
3395 Vector<WebCore::TextAlternativeWithRange> dictationAlternatives;
3397 bool registerUndoGroup = false;
3398 if (isAttributedString) {
3399 #if USE(DICTATION_ALTERNATIVES)
3400 WebCore::collectDictationTextAlternatives(string, dictationAlternatives);
3402 #if USE(INSERTION_UNDO_GROUPING)
3403 registerUndoGroup = WebCore::shouldRegisterInsertionUndoGroup(string);
3405 // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
3406 text = [string string];
3410 // insertText can be called for several reasons:
3411 // - If it's from normal key event processing (including key bindings), we save the action to perform it later.
3412 // - If it's from an input method, then we should insert the text now.
3413 // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
3414 // then we also execute it immediately, as there will be no other chance.
3415 Vector<WebCore::KeypressCommand>* keypressCommands = m_collectedKeypressCommands;
3416 if (keypressCommands) {
3417 ASSERT(replacementRange.location == NSNotFound);
3418 WebCore::KeypressCommand command("insertText:", text);
3419 keypressCommands->append(command);
3420 LOG(TextInput, "...stored");
3421 m_page->registerKeypressCommandName(command.commandName);
3425 String eventText = text;
3426 eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
3427 if (!dictationAlternatives.isEmpty())
3428 m_page->insertDictatedTextAsync(eventText, replacementRange, dictationAlternatives, registerUndoGroup);
3430 m_page->insertTextAsync(eventText, replacementRange, registerUndoGroup);
3433 void WebViewImpl::selectedRangeWithCompletionHandler(void(^completionHandlerPtr)(NSRange selectedRange))
3435 auto completionHandler = adoptNS([completionHandlerPtr copy]);
3437 LOG(TextInput, "selectedRange");
3438 m_page->getSelectedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
3439 void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
3440 if (error != WebKit::CallbackBase::Error::None) {
3441 LOG(TextInput, " ...selectedRange failed.");
3442 completionHandlerBlock(NSMakeRange(NSNotFound, 0));
3445 NSRange result = editingRangeResult;
3446 if (result.location == NSNotFound)
3447 LOG(TextInput, " -> selectedRange returned (NSNotFound, %llu)", result.length);
3449 LOG(TextInput, " -> selectedRange returned (%llu, %llu)", result.location, result.length);
3450 completionHandlerBlock(result);
3454 void WebViewImpl::markedRangeWithCompletionHandler(void(^completionHandlerPtr)(NSRange markedRange))
3456 auto completionHandler = adoptNS([completionHandlerPtr copy]);
3458 LOG(TextInput, "markedRange");
3459 m_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
3460 void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
3461 if (error != WebKit::CallbackBase::Error::None) {
3462 LOG(TextInput, " ...markedRange failed.");
3463 completionHandlerBlock(NSMakeRange(NSNotFound, 0));
3466 NSRange result = editingRangeResult;
3467 if (result.location == NSNotFound)
3468 LOG(TextInput, " -> markedRange returned (NSNotFound, %llu)", result.length);
3470 LOG(TextInput, " -> markedRange returned (%llu, %llu)", result.location, result.length);
3471 completionHandlerBlock(result);
3475 void WebViewImpl::hasMarkedTextWithCompletionHandler(void(^completionHandlerPtr)(BOOL hasMarkedText))
3477 auto completionHandler = adoptNS([completionHandlerPtr copy]);
3479 LOG(TextInput, "hasMarkedText");
3480 m_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
3481 void (^completionHandlerBlock)(BOOL) = (void (^)(BOOL))completionHandler.get();
3482 if (error != WebKit::CallbackBase::Error::None) {
3483 LOG(TextInput, " ...hasMarkedText failed.");
3484 completionHandlerBlock(NO);
3487 BOOL hasMarkedText = editingRangeResult.location != notFound;
3488 LOG(TextInput, " -> hasMarkedText returned %u", hasMarkedText);
3489 completionHandlerBlock(hasMarkedText);
3493 void WebViewImpl::attributedSubstringForProposedRange(NSRange proposedRange, void(^completionHandlerPtr)(NSAttributedString *attrString, NSRange actualRange))
3495 auto completionHandler = adoptNS([completionHandlerPtr copy]);
3497 LOG(TextInput, "attributedSubstringFromRange:(%u, %u)", proposedRange.location, proposedRange.length);
3498 m_page->attributedSubstringForCharacterRangeAsync(proposedRange, [completionHandler](const AttributedString& string, const EditingRange& actualRange, WebKit::CallbackBase::Error error) {
3499 void (^completionHandlerBlock)(NSAttributedString *, NSRange) = (void (^)(NSAttributedString *, NSRange))completionHandler.get();
3500 if (error != WebKit::CallbackBase::Error::None) {
3501 LOG(TextInput, " ...attributedSubstringFromRange failed.");
3502 completionHandlerBlock(0, NSMakeRange(NSNotFound, 0));
3505 LOG(TextInput, " -> attributedSubstringFromRange returned %@", [string.string.get() string]);