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::acceptsFirstMouse(NSEvent *event)
560 // There's a chance that responding to this event will run a nested event loop, and
561 // fetching a new event might release the old one. Retaining and then autoreleasing
562 // the current event prevents that from causing a problem inside WebKit or AppKit code.
563 [[event retain] autorelease];
565 if (![m_view hitTest:event.locationInWindow])
568 setLastMouseDownEvent(event);
569 bool result = m_page->acceptsFirstMouse(event.eventNumber, WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
570 setLastMouseDownEvent(nil);
574 bool WebViewImpl::acceptsFirstResponder()
579 bool WebViewImpl::becomeFirstResponder()
581 // If we just became first responder again, there is no need to do anything,
582 // since resignFirstResponder has correctly detected this situation.
583 if (m_willBecomeFirstResponderAgain) {
584 m_willBecomeFirstResponderAgain = false;
588 NSSelectionDirection direction = [[m_view window] keyViewSelectionDirection];
590 m_inBecomeFirstResponder = true;
592 updateSecureInputState();
593 m_page->viewStateDidChange(WebCore::ViewState::IsFocused);
594 // Restore the selection in the editable region if resigning first responder cleared selection.
595 m_page->restoreSelectionInFocusedEditableElement();
597 m_inBecomeFirstResponder = false;
599 if (direction != NSDirectSelection) {
600 NSEvent *event = [NSApp currentEvent];
601 NSEvent *keyboardEvent = nil;
602 if ([event type] == NSKeyDown || [event type] == NSKeyUp)
603 keyboardEvent = event;
604 m_page->setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, { }), [](WebKit::CallbackBase::Error) { });
609 bool WebViewImpl::resignFirstResponder()
612 // Predict the case where we are losing first responder status only to
613 // gain it back again. We want resignFirstResponder to do nothing in that case.
614 id nextResponder = [[m_view window] _newFirstResponderAfterResigning];
616 // FIXME: This will probably need to change once WKWebView doesn't contain a WKView.
617 if ([nextResponder isKindOfClass:[WKWebView class]] && m_view.superview == nextResponder) {
618 m_willBecomeFirstResponderAgain = true;
623 m_willBecomeFirstResponderAgain = false;
624 m_inResignFirstResponder = true;
626 #if USE(ASYNC_NSTEXTINPUTCLIENT)
627 m_page->confirmCompositionAsync();
629 if (m_page->editorState().hasComposition && !m_page->editorState().shouldIgnoreCompositionSelectionChange)
630 m_page->cancelComposition();
633 notifyInputContextAboutDiscardedComposition();
635 resetSecureInputState();
637 if (!m_page->maintainsInactiveSelection())
638 m_page->clearSelection();
640 m_page->viewStateDidChange(WebCore::ViewState::IsFocused);
642 m_inResignFirstResponder = false;
647 bool WebViewImpl::isFocused() const
649 if (m_inBecomeFirstResponder)
651 if (m_inResignFirstResponder)
653 return m_view.window.firstResponder == m_view;
656 void WebViewImpl::viewWillStartLiveResize()
658 m_page->viewWillStartLiveResize();
660 [m_layoutStrategy willStartLiveResize];
663 void WebViewImpl::viewDidEndLiveResize()
665 m_page->viewWillEndLiveResize();
667 [m_layoutStrategy didEndLiveResize];
670 void WebViewImpl::renewGState()
672 if (m_textIndicatorWindow)
673 dismissContentRelativeChildWindowsWithAnimation(false);
675 // Update the view frame.
677 updateWindowAndViewFrames();
679 updateContentInsetsIfAutomatic();
682 void WebViewImpl::setFrameSize(CGSize)
684 [m_layoutStrategy didChangeFrameSize];
687 void WebViewImpl::disableFrameSizeUpdates()
689 [m_layoutStrategy disableFrameSizeUpdates];
692 void WebViewImpl::enableFrameSizeUpdates()
694 [m_layoutStrategy enableFrameSizeUpdates];
697 bool WebViewImpl::frameSizeUpdatesDisabled() const
699 return [m_layoutStrategy frameSizeUpdatesDisabled];
702 void WebViewImpl::setFrameAndScrollBy(CGRect frame, CGSize offset)
704 ASSERT(CGSizeEqualToSize(m_resizeScrollOffset, CGSizeZero));
706 m_resizeScrollOffset = offset;
707 m_view.frame = NSRectFromCGRect(frame);
710 void WebViewImpl::updateWindowAndViewFrames()
712 if (clipsToVisibleRect())
713 updateViewExposedRect();
715 if (m_didScheduleWindowAndViewFrameUpdate)
718 m_didScheduleWindowAndViewFrameUpdate = true;
720 auto weakThis = createWeakPtr();
721 dispatch_async(dispatch_get_main_queue(), [weakThis] {
725 weakThis->m_didScheduleWindowAndViewFrameUpdate = false;
727 NSRect viewFrameInWindowCoordinates = NSZeroRect;
728 NSPoint accessibilityPosition = NSZeroPoint;
730 if (weakThis->m_needsViewFrameInWindowCoordinates)
731 viewFrameInWindowCoordinates = [weakThis->m_view convertRect:weakThis->m_view.frame toView:nil];
733 #pragma clang diagnostic push
734 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
735 if (WebCore::AXObjectCache::accessibilityEnabled())
736 accessibilityPosition = [[weakThis->m_view accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
737 #pragma clang diagnostic pop
739 weakThis->m_page->windowAndViewFramesChanged(viewFrameInWindowCoordinates, accessibilityPosition);
743 void WebViewImpl::setFixedLayoutSize(CGSize fixedLayoutSize)
745 m_page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
748 CGSize WebViewImpl::fixedLayoutSize() const
750 return m_page->fixedLayoutSize();
753 std::unique_ptr<WebKit::DrawingAreaProxy> WebViewImpl::createDrawingAreaProxy()
755 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"WebKit2UseRemoteLayerTreeDrawingArea"] boolValue])
756 return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(m_page);
758 return std::make_unique<TiledCoreAnimationDrawingAreaProxy>(m_page);
761 bool WebViewImpl::isUsingUISideCompositing() const
763 auto* drawingArea = m_page->drawingArea();
764 return drawingArea && drawingArea->type() == DrawingAreaTypeRemoteLayerTree;
767 void WebViewImpl::setDrawingAreaSize(CGSize size)
769 if (!m_page->drawingArea())
772 m_page->drawingArea()->setSize(WebCore::IntSize(size), WebCore::IntSize(), WebCore::IntSize(m_resizeScrollOffset));
773 m_resizeScrollOffset = CGSizeZero;
776 #pragma clang diagnostic push
777 #pragma clang diagnostic ignored "-Wmissing-noreturn"
778 // This method forces a drawing area geometry update, even if frame size updates are disabled.
779 // The updated is performed asynchronously; we don't wait for the geometry update before returning.
780 // The area drawn need not match the current frame size - if it differs it will be anchored to the
781 // frame according to the current contentAnchor.
782 void WebViewImpl::forceAsyncDrawingAreaSizeUpdate(CGSize size)
784 // This SPI is only used on 10.9 and below, and is incompatible with the fence-based drawing area size synchronization in 10.10+.
785 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
786 if (m_clipsToVisibleRect)
787 updateViewExposedRect();
788 setDrawingAreaSize(size);
790 // If a geometry update is pending the new update won't be sent. Poll without waiting for any
791 // pending did-update message now, such that the new update can be sent. We do so after setting
792 // the drawing area size such that the latest update is sent.
793 if (DrawingAreaProxy* drawingArea = m_page->drawingArea())
794 drawingArea->waitForPossibleGeometryUpdate(std::chrono::milliseconds::zero());
796 ASSERT_NOT_REACHED();
800 void WebViewImpl::waitForAsyncDrawingAreaSizeUpdate()
802 // This SPI is only used on 10.9 and below, and is incompatible with the fence-based drawing area size synchronization in 10.10+.
803 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
804 if (DrawingAreaProxy* drawingArea = m_page->drawingArea()) {
805 // If a geometry update is still pending then the action of receiving the
806 // first geometry update may result in another update being scheduled -
807 // we should wait for this to complete too.
808 drawingArea->waitForPossibleGeometryUpdate(DrawingAreaProxy::didUpdateBackingStoreStateTimeout() / 2);
809 drawingArea->waitForPossibleGeometryUpdate(DrawingAreaProxy::didUpdateBackingStoreStateTimeout() / 2);
812 ASSERT_NOT_REACHED();
815 #pragma clang diagnostic pop
817 void WebViewImpl::updateLayer()
819 m_view.layer.backgroundColor = CGColorGetConstantColor(drawsBackground() ? kCGColorWhite : kCGColorClear);
821 // If asynchronous geometry updates have been sent by forceAsyncDrawingAreaSizeUpdate,
822 // then subsequent calls to setFrameSize should not result in us waiting for the did
823 // udpate response if setFrameSize is called.
824 if (frameSizeUpdatesDisabled())
827 if (DrawingAreaProxy* drawingArea = m_page->drawingArea())
828 drawingArea->waitForPossibleGeometryUpdate();
831 void WebViewImpl::drawRect(CGRect rect)
833 LOG(Printing, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
834 m_page->endPrinting();
837 bool WebViewImpl::canChangeFrameLayout(WebFrameProxy& frame)
839 // PDF documents are already paginated, so we can't change them to add headers and footers.
840 return !frame.isDisplayingPDFDocument();
843 NSPrintOperation *WebViewImpl::printOperationWithPrintInfo(NSPrintInfo *printInfo, WebFrameProxy& frame)
845 LOG(Printing, "Creating an NSPrintOperation for frame '%s'", frame.url().utf8().data());
847 // FIXME: If the frame cannot be printed (e.g. if it contains an encrypted PDF that disallows
848 // printing), this function should return nil.
849 RetainPtr<WKPrintingView> printingView = adoptNS([[WKPrintingView alloc] initWithFrameProxy:&frame view:m_view]);
850 // NSPrintOperation takes ownership of the view.
851 NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get() printInfo:printInfo];
852 [printOperation setCanSpawnSeparateThread:YES];
853 [printOperation setJobTitle:frame.title()];
854 printingView->_printOperation = printOperation;
855 return printOperation;
858 void WebViewImpl::setAutomaticallyAdjustsContentInsets(bool automaticallyAdjustsContentInsets)
860 m_automaticallyAdjustsContentInsets = automaticallyAdjustsContentInsets;
861 updateContentInsetsIfAutomatic();
864 void WebViewImpl::updateContentInsetsIfAutomatic()
866 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
867 if (!m_automaticallyAdjustsContentInsets)
870 NSWindow *window = m_view.window;
871 if ((window.styleMask & NSFullSizeContentViewWindowMask) && !window.titlebarAppearsTransparent && ![m_view enclosingScrollView]) {
872 NSRect contentLayoutRect = [m_view convertRect:window.contentLayoutRect fromView:nil];
873 CGFloat newTopContentInset = NSMaxY(contentLayoutRect) - NSHeight(contentLayoutRect);
874 if (m_topContentInset != newTopContentInset)
875 setTopContentInset(newTopContentInset);
877 setTopContentInset(0);
881 void WebViewImpl::setTopContentInset(CGFloat contentInset)
883 m_topContentInset = contentInset;
885 if (m_didScheduleSetTopContentInset)
888 m_didScheduleSetTopContentInset = true;
890 auto weakThis = createWeakPtr();
891 dispatch_async(dispatch_get_main_queue(), [weakThis] {
894 weakThis->dispatchSetTopContentInset();
898 void WebViewImpl::dispatchSetTopContentInset()
900 if (!m_didScheduleSetTopContentInset)
903 m_didScheduleSetTopContentInset = false;
904 m_page->setTopContentInset(m_topContentInset);
907 void WebViewImpl::prepareContentInRect(CGRect rect)
909 m_contentPreparationRect = rect;
910 m_useContentPreparationRectForVisibleRect = true;
912 updateViewExposedRect();
915 void WebViewImpl::updateViewExposedRect()
917 CGRect exposedRect = NSRectToCGRect([m_view visibleRect]);
919 if (m_useContentPreparationRectForVisibleRect)
920 exposedRect = CGRectUnion(m_contentPreparationRect, exposedRect);
922 if (auto drawingArea = m_page->drawingArea())
923 drawingArea->setExposedRect(m_clipsToVisibleRect ? WebCore::FloatRect(exposedRect) : WebCore::FloatRect::infiniteRect());
926 void WebViewImpl::setClipsToVisibleRect(bool clipsToVisibleRect)
928 m_clipsToVisibleRect = clipsToVisibleRect;
929 updateViewExposedRect();
932 void WebViewImpl::setMinimumSizeForAutoLayout(CGSize minimumSizeForAutoLayout)
934 bool expandsToFit = minimumSizeForAutoLayout.width > 0;
936 m_page->setMinimumLayoutSize(WebCore::IntSize(minimumSizeForAutoLayout));
937 m_page->setMainFrameIsScrollable(!expandsToFit);
939 setClipsToVisibleRect(expandsToFit);
942 CGSize WebViewImpl::minimumSizeForAutoLayout() const
944 return m_page->minimumLayoutSize();
947 void WebViewImpl::setShouldExpandToViewHeightForAutoLayout(bool shouldExpandToViewHeightForAutoLayout)
949 m_page->setAutoSizingShouldExpandToViewHeight(shouldExpandToViewHeightForAutoLayout);
952 bool WebViewImpl::shouldExpandToViewHeightForAutoLayout() const
954 return m_page->autoSizingShouldExpandToViewHeight();
957 void WebViewImpl::setIntrinsicContentSize(CGSize intrinsicContentSize)
959 // If the intrinsic content size is less than the minimum layout width, the content flowed to fit,
960 // so we can report that that dimension is flexible. If not, we need to report our intrinsic width
961 // so that autolayout will know to provide space for us.
963 CGSize intrinsicContentSizeAcknowledgingFlexibleWidth = intrinsicContentSize;
964 if (intrinsicContentSize.width < m_page->minimumLayoutSize().width())
965 intrinsicContentSizeAcknowledgingFlexibleWidth.width = NSViewNoInstrinsicMetric;
967 m_intrinsicContentSize = intrinsicContentSizeAcknowledgingFlexibleWidth;
968 [m_view invalidateIntrinsicContentSize];
971 CGSize WebViewImpl::intrinsicContentSize() const
973 return m_intrinsicContentSize;
976 void WebViewImpl::setViewScale(CGFloat viewScale)
978 m_lastRequestedViewScale = viewScale;
980 if (!supportsArbitraryLayoutModes() && viewScale != 1)
983 if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
984 [NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
986 m_page->scaleView(viewScale);
987 [m_layoutStrategy didChangeViewScale];
990 CGFloat WebViewImpl::viewScale() const
992 return m_page->viewScaleFactor();
995 WKLayoutMode WebViewImpl::layoutMode() const
997 return [m_layoutStrategy layoutMode];
1000 void WebViewImpl::setLayoutMode(WKLayoutMode layoutMode)
1002 m_lastRequestedLayoutMode = layoutMode;
1004 if (!supportsArbitraryLayoutModes() && layoutMode != kWKLayoutModeViewSize)
1007 if (layoutMode == [m_layoutStrategy layoutMode])
1010 [m_layoutStrategy willChangeLayoutStrategy];
1011 m_layoutStrategy = [WKViewLayoutStrategy layoutStrategyWithPage:m_page view:m_view viewImpl:*this mode:layoutMode];
1014 bool WebViewImpl::supportsArbitraryLayoutModes() const
1016 if ([m_fullScreenWindowController isFullScreen])
1019 WebFrameProxy* frame = m_page->mainFrame();
1023 // If we have a plugin document in the main frame, avoid using custom WKLayoutModes
1024 // and fall back to the defaults, because there's a good chance that it won't work (e.g. with PDFPlugin).
1025 if (frame->containsPluginDocument())
1031 void WebViewImpl::updateSupportsArbitraryLayoutModes()
1033 if (!supportsArbitraryLayoutModes()) {
1034 WKLayoutMode oldRequestedLayoutMode = m_lastRequestedLayoutMode;
1035 CGFloat oldRequestedViewScale = m_lastRequestedViewScale;
1037 setLayoutMode(kWKLayoutModeViewSize);
1039 // The 'last requested' parameters will have been overwritten by setting them above, but we don't
1040 // want this to count as a request (only changes from the client count), so reset them.
1041 m_lastRequestedLayoutMode = oldRequestedLayoutMode;
1042 m_lastRequestedViewScale = oldRequestedViewScale;
1043 } else if (m_lastRequestedLayoutMode != [m_layoutStrategy layoutMode]) {
1044 setViewScale(m_lastRequestedViewScale);
1045 setLayoutMode(m_lastRequestedLayoutMode);
1049 void WebViewImpl::setOverrideDeviceScaleFactor(CGFloat deviceScaleFactor)
1051 m_overrideDeviceScaleFactor = deviceScaleFactor;
1052 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1055 float WebViewImpl::intrinsicDeviceScaleFactor() const
1057 if (m_overrideDeviceScaleFactor)
1058 return m_overrideDeviceScaleFactor;
1059 if (m_targetWindowForMovePreparation)
1060 return m_targetWindowForMovePreparation.backingScaleFactor;
1061 if (NSWindow *window = m_view.window)
1062 return window.backingScaleFactor;
1063 return [NSScreen mainScreen].backingScaleFactor;
1066 void WebViewImpl::windowDidOrderOffScreen()
1068 m_page->viewStateDidChange(WebCore::ViewState::IsVisible | WebCore::ViewState::WindowIsActive);
1071 void WebViewImpl::windowDidOrderOnScreen()
1073 m_page->viewStateDidChange(WebCore::ViewState::IsVisible | WebCore::ViewState::WindowIsActive);
1076 void WebViewImpl::windowDidBecomeKey(NSWindow *keyWindow)
1078 if (keyWindow == m_view.window || keyWindow == m_view.window.attachedSheet) {
1079 updateSecureInputState();
1080 m_page->viewStateDidChange(WebCore::ViewState::WindowIsActive);
1084 void WebViewImpl::windowDidResignKey(NSWindow *formerKeyWindow)
1086 if (formerKeyWindow == m_view.window || formerKeyWindow == m_view.window.attachedSheet) {
1087 updateSecureInputState();
1088 m_page->viewStateDidChange(WebCore::ViewState::WindowIsActive);
1092 void WebViewImpl::windowDidMiniaturize()
1094 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1097 void WebViewImpl::windowDidDeminiaturize()
1099 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1102 void WebViewImpl::windowDidMove()
1104 updateWindowAndViewFrames();
1107 void WebViewImpl::windowDidResize()
1109 updateWindowAndViewFrames();
1112 void WebViewImpl::windowDidChangeBackingProperties(CGFloat oldBackingScaleFactor)
1114 CGFloat newBackingScaleFactor = intrinsicDeviceScaleFactor();
1115 if (oldBackingScaleFactor == newBackingScaleFactor)
1118 m_page->setIntrinsicDeviceScaleFactor(newBackingScaleFactor);
1121 void WebViewImpl::windowDidChangeScreen()
1123 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1124 m_page->windowScreenDidChange((PlatformDisplayID)[[[[window screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue]);
1127 void WebViewImpl::windowDidChangeLayerHosting()
1129 m_page->layerHostingModeDidChange();
1132 void WebViewImpl::windowDidChangeOcclusionState()
1134 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1137 bool WebViewImpl::shouldDelayWindowOrderingForEvent(NSEvent *event)
1139 // If this is the active window or we don't have a range selection, there is no need to perform additional checks
1140 // and we can avoid making a synchronous call to the WebProcess.
1141 if (m_view.window.isKeyWindow || m_page->editorState().selectionIsNone || !m_page->editorState().selectionIsRange)
1144 // There's a chance that responding to this event will run a nested event loop, and
1145 // fetching a new event might release the old one. Retaining and then autoreleasing
1146 // the current event prevents that from causing a problem inside WebKit or AppKit code.
1147 [[event retain] autorelease];
1149 if (![m_view hitTest:event.locationInWindow])
1152 setLastMouseDownEvent(event);
1153 bool result = m_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, m_lastPressureEvent.get(), m_view));
1154 setLastMouseDownEvent(nil);
1158 bool WebViewImpl::windowResizeMouseLocationIsInVisibleScrollerThumb(CGPoint point)
1160 NSPoint localPoint = [m_view convertPoint:NSPointFromCGPoint(point) fromView:nil];
1161 NSRect visibleThumbRect = NSRect(m_page->visibleScrollerThumbRect());
1162 return NSMouseInRect(localPoint, visibleThumbRect, m_view.isFlipped);
1165 void WebViewImpl::viewWillMoveToWindow(NSWindow *window)
1167 // If we're in the middle of preparing to move to a window, we should only be moved to that window.
1168 ASSERT(!m_targetWindowForMovePreparation || (m_targetWindowForMovePreparation == window));
1170 NSWindow *currentWindow = m_view.window;
1171 if (window == currentWindow)
1174 clearAllEditCommands();
1176 NSWindow *stopObservingWindow = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1177 [m_windowVisibilityObserver stopObserving:stopObservingWindow];
1178 [m_windowVisibilityObserver startObserving:window];
1181 void WebViewImpl::viewDidMoveToWindow()
1183 NSWindow *window = m_targetWindowForMovePreparation ? m_targetWindowForMovePreparation : m_view.window;
1186 windowDidChangeScreen();
1188 WebCore::ViewState::Flags viewStateChanges = WebCore::ViewState::WindowIsActive | WebCore::ViewState::IsVisible;
1189 if (m_isDeferringViewInWindowChanges)
1190 m_viewInWindowChangeWasDeferred = true;
1192 viewStateChanges |= WebCore::ViewState::IsInWindow;
1193 m_page->viewStateDidChange(viewStateChanges);
1195 updateWindowAndViewFrames();
1197 // FIXME(135509) This call becomes unnecessary once 135509 is fixed; remove.
1198 m_page->layerHostingModeDidChange();
1200 if (!m_flagsChangedEventMonitor) {
1201 auto weakThis = createWeakPtr();
1202 m_flagsChangedEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSFlagsChangedMask handler:[weakThis] (NSEvent *flagsChangedEvent) {
1204 weakThis->postFakeMouseMovedEventForFlagsChangedEvent(flagsChangedEvent);
1205 return flagsChangedEvent;
1209 accessibilityRegisterUIProcessTokens();
1211 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1212 if (m_immediateActionGestureRecognizer && ![[m_view gestureRecognizers] containsObject:m_immediateActionGestureRecognizer.get()] && !m_ignoresNonWheelEvents && m_allowsLinkPreview)
1213 [m_view addGestureRecognizer:m_immediateActionGestureRecognizer.get()];
1216 WebCore::ViewState::Flags viewStateChanges = WebCore::ViewState::WindowIsActive | WebCore::ViewState::IsVisible;
1217 if (m_isDeferringViewInWindowChanges)
1218 m_viewInWindowChangeWasDeferred = true;
1220 viewStateChanges |= WebCore::ViewState::IsInWindow;
1221 m_page->viewStateDidChange(viewStateChanges);
1223 [NSEvent removeMonitor:m_flagsChangedEventMonitor];
1224 m_flagsChangedEventMonitor = nil;
1226 dismissContentRelativeChildWindowsWithAnimation(false);
1228 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1229 if (m_immediateActionGestureRecognizer)
1230 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
1234 m_page->setIntrinsicDeviceScaleFactor(intrinsicDeviceScaleFactor());
1237 void WebViewImpl::viewDidChangeBackingProperties()
1239 NSColorSpace *colorSpace = m_view.window.colorSpace;
1240 if ([colorSpace isEqualTo:m_colorSpace.get()])
1243 m_colorSpace = nullptr;
1244 if (DrawingAreaProxy *drawingArea = m_page->drawingArea())
1245 drawingArea->colorSpaceDidChange();
1248 void WebViewImpl::viewDidHide()
1250 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1253 void WebViewImpl::viewDidUnhide()
1255 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1258 void WebViewImpl::activeSpaceDidChange()
1260 m_page->viewStateDidChange(WebCore::ViewState::IsVisible);
1263 NSView *WebViewImpl::hitTest(CGPoint point)
1265 NSView *hitView = [m_view _web_superHitTest:NSPointFromCGPoint(point)];
1266 if (hitView && hitView == m_layerHostingView)
1272 void WebViewImpl::postFakeMouseMovedEventForFlagsChangedEvent(NSEvent *flagsChangedEvent)
1274 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved location:flagsChangedEvent.window.mouseLocationOutsideOfEventStream
1275 modifierFlags:flagsChangedEvent.modifierFlags timestamp:flagsChangedEvent.timestamp windowNumber:flagsChangedEvent.windowNumber
1276 context:flagsChangedEvent.context eventNumber:0 clickCount:0 pressure:0];
1277 NativeWebMouseEvent webEvent(fakeEvent, m_lastPressureEvent.get(), m_view);
1278 m_page->handleMouseEvent(webEvent);
1281 ColorSpaceData WebViewImpl::colorSpace()
1283 if (!m_colorSpace) {
1284 if (m_targetWindowForMovePreparation)
1285 m_colorSpace = m_targetWindowForMovePreparation.colorSpace;
1286 else if (NSWindow *window = m_view.window)
1287 m_colorSpace = window.colorSpace;
1289 m_colorSpace = [NSScreen mainScreen].colorSpace;
1292 ColorSpaceData colorSpaceData;
1293 colorSpaceData.cgColorSpace = [m_colorSpace CGColorSpace];
1295 return colorSpaceData;
1298 void WebViewImpl::setUnderlayColor(NSColor *underlayColor)
1300 m_page->setUnderlayColor(WebCore::colorFromNSColor(underlayColor));
1303 NSColor *WebViewImpl::underlayColor() const
1305 WebCore::Color webColor = m_page->underlayColor();
1306 if (!webColor.isValid())
1309 return WebCore::nsColor(webColor);
1312 NSColor *WebViewImpl::pageExtendedBackgroundColor() const
1314 WebCore::Color color = m_page->pageExtendedBackgroundColor();
1315 if (!color.isValid())
1318 return WebCore::nsColor(color);
1321 void WebViewImpl::setOverlayScrollbarStyle(WTF::Optional<WebCore::ScrollbarOverlayStyle> scrollbarStyle)
1323 m_page->setOverlayScrollbarStyle(scrollbarStyle);
1326 WTF::Optional<WebCore::ScrollbarOverlayStyle> WebViewImpl::overlayScrollbarStyle() const
1328 return m_page->overlayScrollbarStyle();
1331 void WebViewImpl::beginDeferringViewInWindowChanges()
1333 if (m_shouldDeferViewInWindowChanges) {
1334 NSLog(@"beginDeferringViewInWindowChanges was called while already deferring view-in-window changes!");
1338 m_shouldDeferViewInWindowChanges = true;
1341 void WebViewImpl::endDeferringViewInWindowChanges()
1343 if (!m_shouldDeferViewInWindowChanges) {
1344 NSLog(@"endDeferringViewInWindowChanges was called without beginDeferringViewInWindowChanges!");
1348 m_shouldDeferViewInWindowChanges = false;
1350 if (m_viewInWindowChangeWasDeferred) {
1351 dispatchSetTopContentInset();
1352 m_page->viewStateDidChange(WebCore::ViewState::IsInWindow);
1353 m_viewInWindowChangeWasDeferred = false;
1357 void WebViewImpl::endDeferringViewInWindowChangesSync()
1359 if (!m_shouldDeferViewInWindowChanges) {
1360 NSLog(@"endDeferringViewInWindowChangesSync was called without beginDeferringViewInWindowChanges!");
1364 m_shouldDeferViewInWindowChanges = false;
1366 if (m_viewInWindowChangeWasDeferred) {
1367 dispatchSetTopContentInset();
1368 m_page->viewStateDidChange(WebCore::ViewState::IsInWindow);
1369 m_viewInWindowChangeWasDeferred = false;
1373 void WebViewImpl::prepareForMoveToWindow(NSWindow *targetWindow, std::function<void()> completionHandler)
1375 m_shouldDeferViewInWindowChanges = true;
1376 viewWillMoveToWindow(targetWindow);
1377 m_targetWindowForMovePreparation = targetWindow;
1378 viewDidMoveToWindow();
1380 m_shouldDeferViewInWindowChanges = false;
1382 auto weakThis = createWeakPtr();
1383 m_page->installViewStateChangeCompletionHandler([weakThis, completionHandler]() {
1384 completionHandler();
1389 ASSERT(weakThis->m_view.window == weakThis->m_targetWindowForMovePreparation);
1390 weakThis->m_targetWindowForMovePreparation = nil;
1393 dispatchSetTopContentInset();
1394 m_page->viewStateDidChange(WebCore::ViewState::IsInWindow, false, WebPageProxy::ViewStateChangeDispatchMode::Immediate);
1395 m_viewInWindowChangeWasDeferred = false;
1398 void WebViewImpl::updateSecureInputState()
1400 if (![[m_view window] isKeyWindow] || !isFocused()) {
1401 if (m_inSecureInputState) {
1402 DisableSecureEventInput();
1403 m_inSecureInputState = false;
1407 // WKView has a single input context for all editable areas (except for plug-ins).
1408 NSTextInputContext *context = [m_view _web_superInputContext];
1409 bool isInPasswordField = m_page->editorState().isInPasswordField;
1411 if (isInPasswordField) {
1412 if (!m_inSecureInputState)
1413 EnableSecureEventInput();
1414 static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1];
1415 LOG(TextInput, "-> setAllowedInputSourceLocales:romanInputSources");
1416 [context setAllowedInputSourceLocales:romanInputSources];
1418 if (m_inSecureInputState)
1419 DisableSecureEventInput();
1420 LOG(TextInput, "-> setAllowedInputSourceLocales:nil");
1421 [context setAllowedInputSourceLocales:nil];
1423 m_inSecureInputState = isInPasswordField;
1426 void WebViewImpl::resetSecureInputState()
1428 if (m_inSecureInputState) {
1429 DisableSecureEventInput();
1430 m_inSecureInputState = false;
1434 void WebViewImpl::notifyInputContextAboutDiscardedComposition()
1436 // <rdar://problem/9359055>: -discardMarkedText can only be called for active contexts.
1437 // FIXME: We fail to ever notify the input context if something (e.g. a navigation) happens while the window is not key.
1438 // This is not a problem when the window is key, because we discard marked text on resigning first responder.
1439 if (![[m_view window] isKeyWindow] || m_view != [[m_view window] firstResponder])
1442 LOG(TextInput, "-> discardMarkedText");
1444 [[m_view _web_superInputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to.
1447 void WebViewImpl::setPluginComplexTextInputState(PluginComplexTextInputState pluginComplexTextInputState)
1449 m_pluginComplexTextInputState = pluginComplexTextInputState;
1451 if (m_pluginComplexTextInputState != PluginComplexTextInputDisabled)
1454 // Send back an empty string to the plug-in. This will disable text input.
1455 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, String());
1458 void WebViewImpl::setPluginComplexTextInputStateAndIdentifier(PluginComplexTextInputState pluginComplexTextInputState, uint64_t pluginComplexTextInputIdentifier)
1460 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier) {
1461 // We're asked to update the state for a plug-in that doesn't have focus.
1465 setPluginComplexTextInputState(pluginComplexTextInputState);
1468 void WebViewImpl::disableComplexTextInputIfNecessary()
1470 if (!m_pluginComplexTextInputIdentifier)
1473 if (m_pluginComplexTextInputState != PluginComplexTextInputEnabled)
1476 // Check if the text input window has been dismissed.
1477 if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText])
1478 setPluginComplexTextInputState(PluginComplexTextInputDisabled);
1481 bool WebViewImpl::handlePluginComplexTextInputKeyDown(NSEvent *event)
1483 ASSERT(m_pluginComplexTextInputIdentifier);
1484 ASSERT(m_pluginComplexTextInputState != PluginComplexTextInputDisabled);
1486 BOOL usingLegacyCocoaTextInput = m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy;
1488 NSString *string = nil;
1489 BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string];
1492 m_page->sendComplexTextInputToPlugin(m_pluginComplexTextInputIdentifier, string);
1494 if (!usingLegacyCocoaTextInput)
1495 m_pluginComplexTextInputState = PluginComplexTextInputDisabled;
1498 return didHandleEvent;
1501 bool WebViewImpl::tryHandlePluginComplexTextInputKeyDown(NSEvent *event)
1503 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
1506 // Check if the text input window has been dismissed and let the plug-in process know.
1507 // This is only valid with the updated Cocoa text input spec.
1508 disableComplexTextInputIfNecessary();
1510 // Try feeding the keyboard event directly to the plug-in.
1511 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
1512 return handlePluginComplexTextInputKeyDown(event);
1517 void WebViewImpl::pluginFocusOrWindowFocusChanged(bool pluginHasFocusAndWindowHasFocus, uint64_t pluginComplexTextInputIdentifier)
1519 BOOL inputSourceChanged = m_pluginComplexTextInputIdentifier;
1521 if (pluginHasFocusAndWindowHasFocus) {
1522 // Check if we're already allowing text input for this plug-in.
1523 if (pluginComplexTextInputIdentifier == m_pluginComplexTextInputIdentifier)
1526 m_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier;
1529 // Check if we got a request to unfocus a plug-in that isn't focused.
1530 if (pluginComplexTextInputIdentifier != m_pluginComplexTextInputIdentifier)
1533 m_pluginComplexTextInputIdentifier = 0;
1536 if (inputSourceChanged) {
1537 // The input source changed; discard any entered text.
1538 [[WKTextInputWindowController sharedTextInputWindowController] unmarkText];
1541 // This will force the current input context to be updated to its correct value.
1542 [NSApp updateWindows];
1545 bool WebViewImpl::tryPostProcessPluginComplexTextInputKeyDown(NSEvent *event)
1547 if (!m_pluginComplexTextInputIdentifier || m_pluginComplexTextInputState == PluginComplexTextInputDisabled)
1550 // In the legacy text input model, the event has already been sent to the input method.
1551 if (m_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy)
1554 return handlePluginComplexTextInputKeyDown(event);
1557 void WebViewImpl::handleAcceptedAlternativeText(const String& acceptedAlternative)
1559 m_page->handleAlternativeTextUIResult(acceptedAlternative);
1563 NSInteger WebViewImpl::spellCheckerDocumentTag()
1565 if (!m_spellCheckerDocumentTag)
1566 m_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
1567 return m_spellCheckerDocumentTag.value();
1570 void WebViewImpl::pressureChangeWithEvent(NSEvent *event)
1572 #if defined(__LP64__) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101003
1573 if (event == m_lastPressureEvent)
1576 if (m_ignoresNonWheelEvents)
1579 if (event.phase != NSEventPhaseChanged && event.phase != NSEventPhaseBegan && event.phase != NSEventPhaseEnded)
1582 NativeWebMouseEvent webEvent(event, m_lastPressureEvent.get(), m_view);
1583 m_page->handleMouseEvent(webEvent);
1585 m_lastPressureEvent = event;
1589 #if ENABLE(FULLSCREEN_API)
1590 bool WebViewImpl::hasFullScreenWindowController() const
1592 return !!m_fullScreenWindowController;
1595 WKFullScreenWindowController *WebViewImpl::fullScreenWindowController()
1597 if (!m_fullScreenWindowController)
1598 m_fullScreenWindowController = adoptNS([[WKFullScreenWindowController alloc] initWithWindow:createFullScreenWindow() webView:m_view page:m_page]);
1600 return m_fullScreenWindowController.get();
1603 void WebViewImpl::closeFullScreenWindowController()
1605 if (!m_fullScreenWindowController)
1608 [m_fullScreenWindowController close];
1609 m_fullScreenWindowController = nullptr;
1613 NSView *WebViewImpl::fullScreenPlaceholderView()
1615 #if ENABLE(FULLSCREEN_API)
1616 if (m_fullScreenWindowController && [m_fullScreenWindowController isFullScreen])
1617 return [m_fullScreenWindowController webViewPlaceholder];
1622 NSWindow *WebViewImpl::createFullScreenWindow()
1624 #if ENABLE(FULLSCREEN_API)
1625 return [[[WebCoreFullScreenWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] styleMask:(NSBorderlessWindowMask | NSResizableWindowMask) backing:NSBackingStoreBuffered defer:NO] autorelease];
1631 bool WebViewImpl::isEditable() const
1633 return m_page->isEditable();
1636 typedef HashMap<SEL, String> SelectorNameMap;
1638 // Map selectors into Editor command names.
1639 // This is not needed for any selectors that have the same name as the Editor command.
1640 static const SelectorNameMap& selectorExceptionMap()
1642 static NeverDestroyed<SelectorNameMap> map;
1644 struct SelectorAndCommandName {
1646 ASCIILiteral commandName;
1649 static const SelectorAndCommandName names[] = {
1650 { @selector(insertNewlineIgnoringFieldEditor:), ASCIILiteral("InsertNewline") },
1651 { @selector(insertParagraphSeparator:), ASCIILiteral("InsertNewline") },
1652 { @selector(insertTabIgnoringFieldEditor:), ASCIILiteral("InsertTab") },
1653 { @selector(pageDown:), ASCIILiteral("MovePageDown") },
1654 { @selector(pageDownAndModifySelection:), ASCIILiteral("MovePageDownAndModifySelection") },
1655 { @selector(pageUp:), ASCIILiteral("MovePageUp") },
1656 { @selector(pageUpAndModifySelection:), ASCIILiteral("MovePageUpAndModifySelection") },
1657 { @selector(scrollPageDown:), ASCIILiteral("ScrollPageForward") },
1658 { @selector(scrollPageUp:), ASCIILiteral("ScrollPageBackward") }
1661 for (auto& name : names)
1662 map.get().add(name.selector, name.commandName);
1667 static String commandNameForSelector(SEL selector)
1669 // Check the exception map first.
1670 static const SelectorNameMap& exceptionMap = selectorExceptionMap();
1671 SelectorNameMap::const_iterator it = exceptionMap.find(selector);
1672 if (it != exceptionMap.end())
1675 // Remove the trailing colon.
1676 // No need to capitalize the command name since Editor command names are
1677 // not case sensitive.
1678 const char* selectorName = sel_getName(selector);
1679 size_t selectorNameLength = strlen(selectorName);
1680 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
1682 return String(selectorName, selectorNameLength - 1);
1685 bool WebViewImpl::executeSavedCommandBySelector(SEL selector)
1687 LOG(TextInput, "Executing previously saved command %s", sel_getName(selector));
1688 // The sink does two things: 1) Tells us if the responder went unhandled, and
1689 // 2) prevents any NSBeep; we don't ever want to beep here.
1690 RetainPtr<WKResponderChainSink> sink = adoptNS([[WKResponderChainSink alloc] initWithResponderChain:m_view]);
1691 [m_view _web_superDoCommandBySelector:selector];
1693 return ![sink didReceiveUnhandledCommand];
1696 void WebViewImpl::executeEditCommandForSelector(SEL selector, const String& argument)
1698 m_page->executeEditCommand(commandNameForSelector(selector), argument);
1701 void WebViewImpl::registerEditCommand(RefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo)
1703 RefPtr<WebEditCommandProxy> command = prpCommand;
1705 RetainPtr<WKEditCommandObjC> commandObjC = adoptNS([[WKEditCommandObjC alloc] initWithWebEditCommandProxy:command]);
1706 String actionName = WebEditCommandProxy::nameForEditAction(command->editAction());
1708 NSUndoManager *undoManager = [m_view undoManager];
1709 [undoManager registerUndoWithTarget:m_undoTarget.get() selector:((undoOrRedo == WebPageProxy::Undo) ? @selector(undoEditing:) : @selector(redoEditing:)) object:commandObjC.get()];
1710 if (!actionName.isEmpty())
1711 [undoManager setActionName:(NSString *)actionName];
1714 void WebViewImpl::clearAllEditCommands()
1716 [[m_view undoManager] removeAllActionsWithTarget:m_undoTarget.get()];
1719 bool WebViewImpl::writeSelectionToPasteboard(NSPasteboard *pasteboard, NSArray *types)
1721 size_t numTypes = types.count;
1722 [pasteboard declareTypes:types owner:nil];
1723 for (size_t i = 0; i < numTypes; ++i) {
1724 if ([[types objectAtIndex:i] isEqualTo:NSStringPboardType])
1725 [pasteboard setString:m_page->stringSelectionForPasteboard() forType:NSStringPboardType];
1727 RefPtr<WebCore::SharedBuffer> buffer = m_page->dataSelectionForPasteboard([types objectAtIndex:i]);
1728 [pasteboard setData:buffer ? buffer->createNSData().get() : nil forType:[types objectAtIndex:i]];
1734 bool WebViewImpl::readSelectionFromPasteboard(NSPasteboard *pasteboard)
1736 return m_page->readSelectionFromPasteboard([pasteboard name]);
1739 id WebViewImpl::validRequestorForSendAndReturnTypes(NSString *sendType, NSString *returnType)
1741 EditorState editorState = m_page->editorState();
1742 bool isValidSendType = false;
1744 if (sendType && !editorState.selectionIsNone) {
1745 if (editorState.isInPlugin)
1746 isValidSendType = [sendType isEqualToString:NSStringPboardType];
1748 isValidSendType = [PasteboardTypes::forSelection() containsObject:sendType];
1751 bool isValidReturnType = false;
1753 isValidReturnType = true;
1754 else if ([PasteboardTypes::forEditing() containsObject:returnType] && editorState.isContentEditable) {
1755 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts.
1756 isValidReturnType = editorState.isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType];
1758 if (isValidSendType && isValidReturnType)
1760 return [[m_view nextResponder] validRequestorForSendType:sendType returnType:returnType];
1763 void WebViewImpl::centerSelectionInVisibleArea()
1765 m_page->centerSelectionInVisibleArea();
1768 void WebViewImpl::selectionDidChange()
1770 updateFontPanelIfNeeded();
1773 void WebViewImpl::startObservingFontPanel()
1775 [m_windowVisibilityObserver startObservingFontPanel];
1778 void WebViewImpl::updateFontPanelIfNeeded()
1780 const EditorState& editorState = m_page->editorState();
1781 if (editorState.selectionIsNone || !editorState.isContentEditable)
1783 if ([NSFontPanel sharedFontPanelExists] && [[NSFontPanel sharedFontPanel] isVisible]) {
1784 m_page->fontAtSelection([](const String& fontName, double fontSize, bool selectionHasMultipleFonts, WebKit::CallbackBase::Error error) {
1785 NSFont *font = [NSFont fontWithName:fontName size:fontSize];
1787 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:selectionHasMultipleFonts];
1792 void WebViewImpl::changeFontFromFontPanel()
1794 NSFontManager *fontManager = [NSFontManager sharedFontManager];
1795 NSFont *font = [fontManager convertFont:fontManager.selectedFont];
1798 m_page->setFont(font.familyName, font.pointSize, font.fontDescriptor.symbolicTraits);
1801 static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item)
1803 if (![(NSObject *)item isKindOfClass:[NSMenuItem class]])
1805 return (NSMenuItem *)item;
1808 static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item)
1810 if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]])
1812 return (NSToolbarItem *)item;
1815 bool WebViewImpl::validateUserInterfaceItem(id <NSValidatedUserInterfaceItem> item)
1817 SEL action = [item action];
1819 if (action == @selector(showGuessPanel:)) {
1820 if (NSMenuItem *menuItem = WebKit::menuItem(item))
1821 [menuItem setTitle:WebCore::contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])];
1822 return m_page->editorState().isContentEditable;
1825 if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:))
1826 return m_page->editorState().isContentEditable;
1828 if (action == @selector(toggleContinuousSpellChecking:)) {
1829 bool enabled = TextChecker::isContinuousSpellCheckingAllowed();
1830 bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled;
1831 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1835 if (action == @selector(toggleGrammarChecking:)) {
1836 bool checked = TextChecker::state().isGrammarCheckingEnabled;
1837 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1841 if (action == @selector(toggleAutomaticSpellingCorrection:)) {
1842 bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled;
1843 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1844 return m_page->editorState().isContentEditable;
1847 if (action == @selector(orderFrontSubstitutionsPanel:)) {
1848 if (NSMenuItem *menuItem = WebKit::menuItem(item))
1849 [menuItem setTitle:WebCore::contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])];
1850 return m_page->editorState().isContentEditable;
1853 if (action == @selector(toggleSmartInsertDelete:)) {
1854 bool checked = m_page->isSmartInsertDeleteEnabled();
1855 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1856 return m_page->editorState().isContentEditable;
1859 if (action == @selector(toggleAutomaticQuoteSubstitution:)) {
1860 bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
1861 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1862 return m_page->editorState().isContentEditable;
1865 if (action == @selector(toggleAutomaticDashSubstitution:)) {
1866 bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled;
1867 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1868 return m_page->editorState().isContentEditable;
1871 if (action == @selector(toggleAutomaticLinkDetection:)) {
1872 bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled;
1873 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1874 return m_page->editorState().isContentEditable;
1877 if (action == @selector(toggleAutomaticTextReplacement:)) {
1878 bool checked = TextChecker::state().isAutomaticTextReplacementEnabled;
1879 [menuItem(item) setState:checked ? NSOnState : NSOffState];
1880 return m_page->editorState().isContentEditable;
1883 if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:))
1884 return m_page->editorState().selectionIsRange && m_page->editorState().isContentEditable;
1886 if (action == @selector(stopSpeaking:))
1887 return [NSApp isSpeaking];
1889 // The centerSelectionInVisibleArea: selector is enabled if there's a selection range or if there's an insertion point in an editable area.
1890 if (action == @selector(centerSelectionInVisibleArea:))
1891 return m_page->editorState().selectionIsRange || (m_page->editorState().isContentEditable && !m_page->editorState().selectionIsNone);
1893 // Next, handle editor commands. Start by returning true for anything that is not an editor command.
1894 // Returning true is the default thing to do in an AppKit validate method for any selector that is not recognized.
1895 String commandName = commandNameForSelector([item action]);
1896 if (!WebCore::Editor::commandIsSupportedFromMenuOrKeyBinding(commandName))
1899 // Add this item to the vector of items for a given command that are awaiting validation.
1900 ValidationMap::AddResult addResult = m_validationMap.add(commandName, ValidationVector());
1901 addResult.iterator->value.append(item);
1902 if (addResult.isNewEntry) {
1903 // If we are not already awaiting validation for this command, start the asynchronous validation process.
1904 // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time
1905 // we asked for the same command; there is no guarantee the answer is still valid.
1906 auto weakThis = createWeakPtr();
1907 m_page->validateCommand(commandName, [weakThis](const String& commandName, bool isEnabled, int32_t state, WebKit::CallbackBase::Error error) {
1911 // If the process exits before the command can be validated, we'll be called back with an error.
1912 if (error != WebKit::CallbackBase::Error::None)
1915 weakThis->setUserInterfaceItemState(commandName, isEnabled, state);
1919 // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called.
1920 // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment.
1921 // But returning NO here would be worse; that would make keyboard commands such as command-C fail.
1925 void WebViewImpl::setUserInterfaceItemState(NSString *commandName, bool enabled, int state)
1927 ValidationVector items = m_validationMap.take(commandName);
1928 for (auto& item : items) {
1929 [menuItem(item.get()) setState:state];
1930 [menuItem(item.get()) setEnabled:enabled];
1931 [toolbarItem(item.get()) setEnabled:enabled];
1932 // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled.
1936 void WebViewImpl::startSpeaking()
1938 m_page->getSelectionOrContentsAsString([](const String& string, WebKit::CallbackBase::Error error) {
1939 if (error != WebKit::CallbackBase::Error::None)
1944 [NSApp speakString:string];
1948 void WebViewImpl::stopSpeaking(id sender)
1950 [NSApp stopSpeaking:sender];
1953 void WebViewImpl::showGuessPanel(id sender)
1955 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
1957 LOG_ERROR("No NSSpellChecker");
1961 NSPanel *spellingPanel = [checker spellingPanel];
1962 if ([spellingPanel isVisible]) {
1963 [spellingPanel orderOut:sender];
1967 m_page->advanceToNextMisspelling(true);
1968 [spellingPanel orderFront:sender];
1971 void WebViewImpl::checkSpelling()
1973 m_page->advanceToNextMisspelling(false);
1976 void WebViewImpl::changeSpelling(id sender)
1978 NSString *word = [[sender selectedCell] stringValue];
1980 m_page->changeSpellingToWord(word);
1983 void WebViewImpl::toggleContinuousSpellChecking()
1985 bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled;
1986 TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled);
1988 m_page->process().updateTextCheckerState();
1991 bool WebViewImpl::isGrammarCheckingEnabled()
1993 return TextChecker::state().isGrammarCheckingEnabled;
1996 void WebViewImpl::setGrammarCheckingEnabled(bool flag)
1998 if (static_cast<bool>(flag) == TextChecker::state().isGrammarCheckingEnabled)
2001 TextChecker::setGrammarCheckingEnabled(flag);
2002 m_page->process().updateTextCheckerState();
2005 void WebViewImpl::toggleGrammarChecking()
2007 bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled;
2008 TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled);
2010 m_page->process().updateTextCheckerState();
2013 void WebViewImpl::toggleAutomaticSpellingCorrection()
2015 TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled);
2017 m_page->process().updateTextCheckerState();
2020 void WebViewImpl::orderFrontSubstitutionsPanel(id sender)
2022 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2024 LOG_ERROR("No NSSpellChecker");
2028 NSPanel *substitutionsPanel = [checker substitutionsPanel];
2029 if ([substitutionsPanel isVisible]) {
2030 [substitutionsPanel orderOut:sender];
2033 [substitutionsPanel orderFront:sender];
2036 void WebViewImpl::toggleSmartInsertDelete()
2038 m_page->setSmartInsertDeleteEnabled(!m_page->isSmartInsertDeleteEnabled());
2041 bool WebViewImpl::isAutomaticQuoteSubstitutionEnabled()
2043 return TextChecker::state().isAutomaticQuoteSubstitutionEnabled;
2046 void WebViewImpl::setAutomaticQuoteSubstitutionEnabled(bool flag)
2048 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled)
2051 TextChecker::setAutomaticQuoteSubstitutionEnabled(flag);
2052 m_page->process().updateTextCheckerState();
2055 void WebViewImpl::toggleAutomaticQuoteSubstitution()
2057 TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled);
2058 m_page->process().updateTextCheckerState();
2061 bool WebViewImpl::isAutomaticDashSubstitutionEnabled()
2063 return TextChecker::state().isAutomaticDashSubstitutionEnabled;
2066 void WebViewImpl::setAutomaticDashSubstitutionEnabled(bool flag)
2068 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled)
2071 TextChecker::setAutomaticDashSubstitutionEnabled(flag);
2072 m_page->process().updateTextCheckerState();
2075 void WebViewImpl::toggleAutomaticDashSubstitution()
2077 TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled);
2078 m_page->process().updateTextCheckerState();
2081 bool WebViewImpl::isAutomaticLinkDetectionEnabled()
2083 return TextChecker::state().isAutomaticLinkDetectionEnabled;
2086 void WebViewImpl::setAutomaticLinkDetectionEnabled(bool flag)
2088 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled)
2091 TextChecker::setAutomaticLinkDetectionEnabled(flag);
2092 m_page->process().updateTextCheckerState();
2095 void WebViewImpl::toggleAutomaticLinkDetection()
2097 TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled);
2098 m_page->process().updateTextCheckerState();
2101 bool WebViewImpl::isAutomaticTextReplacementEnabled()
2103 return TextChecker::state().isAutomaticTextReplacementEnabled;
2106 void WebViewImpl::setAutomaticTextReplacementEnabled(bool flag)
2108 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled)
2111 TextChecker::setAutomaticTextReplacementEnabled(flag);
2112 m_page->process().updateTextCheckerState();
2115 void WebViewImpl::toggleAutomaticTextReplacement()
2117 TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled);
2118 m_page->process().updateTextCheckerState();
2121 void WebViewImpl::uppercaseWord()
2123 m_page->uppercaseWord();
2126 void WebViewImpl::lowercaseWord()
2128 m_page->lowercaseWord();
2131 void WebViewImpl::capitalizeWord()
2133 m_page->capitalizeWord();
2136 void WebViewImpl::preferencesDidChange()
2138 BOOL needsViewFrameInWindowCoordinates = m_page->preferences().pluginsEnabled();
2140 if (!!needsViewFrameInWindowCoordinates == !!m_needsViewFrameInWindowCoordinates)
2143 m_needsViewFrameInWindowCoordinates = needsViewFrameInWindowCoordinates;
2145 updateWindowAndViewFrames();
2148 void WebViewImpl::setTextIndicator(WebCore::TextIndicator& textIndicator, WebCore::TextIndicatorWindowLifetime lifetime)
2150 if (!m_textIndicatorWindow)
2151 m_textIndicatorWindow = std::make_unique<WebCore::TextIndicatorWindow>(m_view);
2153 NSRect textBoundingRectInScreenCoordinates = [m_view.window convertRectToScreen:[m_view convertRect:textIndicator.textBoundingRectInRootViewCoordinates() toView:nil]];
2154 m_textIndicatorWindow->setTextIndicator(textIndicator, NSRectToCGRect(textBoundingRectInScreenCoordinates), lifetime);
2157 void WebViewImpl::clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation animation)
2159 if (m_textIndicatorWindow)
2160 m_textIndicatorWindow->clearTextIndicator(animation);
2161 m_textIndicatorWindow = nullptr;
2164 void WebViewImpl::setTextIndicatorAnimationProgress(float progress)
2166 if (m_textIndicatorWindow)
2167 m_textIndicatorWindow->setAnimationProgress(progress);
2170 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimation(bool animate)
2172 [m_view _web_dismissContentRelativeChildWindowsWithAnimation:animate];
2175 void WebViewImpl::dismissContentRelativeChildWindowsWithAnimationFromViewOnly(bool animate)
2177 // Calling _clearTextIndicatorWithAnimation here will win out over the animated clear in dismissContentRelativeChildWindowsFromViewOnly.
2178 // We can't invert these because clients can override (and have overridden) _dismissContentRelativeChildWindows, so it needs to be called.
2179 // For this same reason, this can't be moved to WebViewImpl without care.
2180 clearTextIndicatorWithAnimation(animate ? WebCore::TextIndicatorWindowDismissalAnimation::FadeOut : WebCore::TextIndicatorWindowDismissalAnimation::None);
2181 [m_view _web_dismissContentRelativeChildWindows];
2184 void WebViewImpl::dismissContentRelativeChildWindowsFromViewOnly()
2186 bool hasActiveImmediateAction = false;
2187 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2188 hasActiveImmediateAction = [m_immediateActionController hasActiveImmediateAction];
2191 // FIXME: We don't know which panel we are dismissing, it may not even be in the current page (see <rdar://problem/13875766>).
2192 if (m_view.window.isKeyWindow || hasActiveImmediateAction) {
2193 WebCore::DictionaryLookup::hidePopup();
2195 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2196 DDActionsManager *actionsManager = [getDDActionsManagerClass() sharedManager];
2197 if ([actionsManager respondsToSelector:@selector(requestBubbleClosureUnanchorOnFailure:)])
2198 [actionsManager requestBubbleClosureUnanchorOnFailure:YES];
2202 clearTextIndicatorWithAnimation(WebCore::TextIndicatorWindowDismissalAnimation::FadeOut);
2204 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2205 [m_immediateActionController dismissContentRelativeChildWindows];
2208 m_pageClient->dismissCorrectionPanel(WebCore::ReasonForDismissingAlternativeTextIgnored);
2211 void WebViewImpl::hideWordDefinitionWindow()
2213 WebCore::DictionaryLookup::hidePopup();
2216 void WebViewImpl::quickLookWithEvent(NSEvent *event)
2218 if (ignoresNonWheelEvents())
2221 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2222 if (m_immediateActionGestureRecognizer) {
2223 [m_view _web_superQuickLookWithEvent:event];
2228 NSPoint locationInViewCoordinates = [m_view convertPoint:[event locationInWindow] fromView:nil];
2229 m_page->performDictionaryLookupAtLocation(WebCore::FloatPoint(locationInViewCoordinates));
2232 void WebViewImpl::prepareForDictionaryLookup()
2234 if (m_didRegisterForLookupPopoverCloseNotifications)
2237 m_didRegisterForLookupPopoverCloseNotifications = true;
2239 [m_windowVisibilityObserver startObservingLookupDismissal];
2242 void WebViewImpl::setAllowsLinkPreview(bool allowsLinkPreview)
2244 if (m_allowsLinkPreview == allowsLinkPreview)
2247 m_allowsLinkPreview = allowsLinkPreview;
2249 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2250 if (!allowsLinkPreview)
2251 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
2252 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get())
2253 [m_view addGestureRecognizer:immediateActionRecognizer];
2257 void* WebViewImpl::immediateActionAnimationControllerForHitTestResult(API::HitTestResult* hitTestResult, uint32_t type, API::Object* userData)
2259 return [m_view _web_immediateActionAnimationControllerForHitTestResultInternal:hitTestResult withType:type userData:userData];
2262 void WebViewImpl::didPerformImmediateActionHitTest(const WebHitTestResultData& result, bool contentPreventsDefault, API::Object* userData)
2264 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2265 [m_immediateActionController didPerformImmediateActionHitTest:result contentPreventsDefault:contentPreventsDefault userData:userData];
2269 void WebViewImpl::prepareForImmediateActionAnimation()
2271 [m_view _web_prepareForImmediateActionAnimation];
2274 void WebViewImpl::cancelImmediateActionAnimation()
2276 [m_view _web_cancelImmediateActionAnimation];
2279 void WebViewImpl::completeImmediateActionAnimation()
2281 [m_view _web_completeImmediateActionAnimation];
2284 void WebViewImpl::didChangeContentSize(CGSize newSize)
2286 [m_view _web_didChangeContentSize:NSSizeFromCGSize(newSize)];
2289 void WebViewImpl::setIgnoresNonWheelEvents(bool ignoresNonWheelEvents)
2291 if (m_ignoresNonWheelEvents == ignoresNonWheelEvents)
2294 m_ignoresNonWheelEvents = ignoresNonWheelEvents;
2295 m_page->setShouldDispatchFakeMouseMoveEvents(!ignoresNonWheelEvents);
2297 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2298 if (ignoresNonWheelEvents)
2299 [m_view removeGestureRecognizer:m_immediateActionGestureRecognizer.get()];
2300 else if (NSGestureRecognizer *immediateActionRecognizer = m_immediateActionGestureRecognizer.get()) {
2301 if (m_allowsLinkPreview)
2302 [m_view addGestureRecognizer:immediateActionRecognizer];
2307 void WebViewImpl::setIgnoresAllEvents(bool ignoresAllEvents)
2309 m_ignoresAllEvents = ignoresAllEvents;
2310 setIgnoresNonWheelEvents(ignoresAllEvents);
2313 void WebViewImpl::setIgnoresMouseDraggedEvents(bool ignoresMouseDraggedEvents)
2315 m_ignoresMouseDraggedEvents = ignoresMouseDraggedEvents;
2318 void WebViewImpl::setAccessibilityWebProcessToken(NSData *data)
2320 m_remoteAccessibilityChild = WKAXRemoteElementForToken(data);
2321 updateRemoteAccessibilityRegistration(true);
2324 void WebViewImpl::updateRemoteAccessibilityRegistration(bool registerProcess)
2326 // When the tree is connected/disconnected, the remote accessibility registration
2327 // needs to be updated with the pid of the remote process. If the process is going
2328 // away, that information is not present in WebProcess
2330 if (registerProcess)
2331 pid = m_page->process().processIdentifier();
2332 else if (!registerProcess) {
2333 pid = WKAXRemoteProcessIdentifier(m_remoteAccessibilityChild.get());
2334 m_remoteAccessibilityChild = nil;
2337 WKAXRegisterRemoteProcess(registerProcess, pid);
2340 void WebViewImpl::accessibilityRegisterUIProcessTokens()
2342 // Initialize remote accessibility when the window connection has been established.
2343 NSData *remoteElementToken = WKAXRemoteTokenForElement(m_view);
2344 NSData *remoteWindowToken = WKAXRemoteTokenForElement(m_view.window);
2345 IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]);
2346 IPC::DataReference windowToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]);
2347 m_page->registerUIProcessAccessibilityTokens(elementToken, windowToken);
2350 id WebViewImpl::accessibilityFocusedUIElement()
2352 enableAccessibilityIfNecessary();
2353 return m_remoteAccessibilityChild.get();
2356 id WebViewImpl::accessibilityHitTest(CGPoint)
2358 return accessibilityFocusedUIElement();
2361 void WebViewImpl::enableAccessibilityIfNecessary()
2363 if (WebCore::AXObjectCache::accessibilityEnabled())
2366 // After enabling accessibility update the window frame on the web process so that the
2367 // correct accessibility position is transmitted (when AX is off, that position is not calculated).
2368 WebCore::AXObjectCache::enableAccessibility();
2369 updateWindowAndViewFrames();
2372 id WebViewImpl::accessibilityAttributeValue(NSString *attribute)
2374 enableAccessibilityIfNecessary();
2376 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
2379 if (m_remoteAccessibilityChild)
2380 child = m_remoteAccessibilityChild.get();
2384 return [NSArray arrayWithObject:child];
2386 if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
2387 return NSAccessibilityGroupRole;
2388 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
2389 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
2390 if ([attribute isEqualToString:NSAccessibilityParentAttribute])
2391 return NSAccessibilityUnignoredAncestor([m_view superview]);
2392 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
2395 return [m_view _web_superAccessibilityAttributeValue:attribute];
2398 void WebViewImpl::setPrimaryTrackingArea(NSTrackingArea *trackingArea)
2400 [m_view removeTrackingArea:m_primaryTrackingArea.get()];
2401 m_primaryTrackingArea = trackingArea;
2402 [m_view addTrackingArea:trackingArea];
2405 // Any non-zero value will do, but using something recognizable might help us debug some day.
2406 #define TRACKING_RECT_TAG 0xBADFACE
2408 NSTrackingRectTag WebViewImpl::addTrackingRect(CGRect, id owner, void* userData, bool assumeInside)
2410 ASSERT(m_trackingRectOwner == nil);
2411 m_trackingRectOwner = owner;
2412 m_trackingRectUserData = userData;
2413 return TRACKING_RECT_TAG;
2416 NSTrackingRectTag WebViewImpl::addTrackingRectWithTrackingNum(CGRect, id owner, void* userData, bool assumeInside, int tag)
2418 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG);
2419 ASSERT(m_trackingRectOwner == nil);
2420 m_trackingRectOwner = owner;
2421 m_trackingRectUserData = userData;
2422 return TRACKING_RECT_TAG;
2425 void WebViewImpl::addTrackingRectsWithTrackingNums(CGRect*, id owner, void** userDataList, bool assumeInside, NSTrackingRectTag *trackingNums, int count)
2428 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG);
2429 ASSERT(m_trackingRectOwner == nil);
2430 m_trackingRectOwner = owner;
2431 m_trackingRectUserData = userDataList[0];
2432 trackingNums[0] = TRACKING_RECT_TAG;
2435 void WebViewImpl::removeTrackingRect(NSTrackingRectTag tag)
2440 if (tag == TRACKING_RECT_TAG) {
2441 m_trackingRectOwner = nil;
2445 if (tag == m_lastToolTipTag) {
2446 [m_view _web_superRemoveTrackingRect:tag];
2447 m_lastToolTipTag = 0;
2451 // If any other tracking rect is being removed, we don't know how it was created
2452 // and it's possible there's a leak involved (see 3500217)
2453 ASSERT_NOT_REACHED();
2456 void WebViewImpl::removeTrackingRects(NSTrackingRectTag *tags, int count)
2458 for (int i = 0; i < count; ++i) {
2462 ASSERT(tag == TRACKING_RECT_TAG);
2463 m_trackingRectOwner = nil;
2467 void WebViewImpl::sendToolTipMouseExited()
2469 // Nothing matters except window, trackingNumber, and userData.
2470 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited
2471 location:NSMakePoint(0, 0)
2474 windowNumber:m_view.window.windowNumber
2477 trackingNumber:TRACKING_RECT_TAG
2478 userData:m_trackingRectUserData];
2479 [m_trackingRectOwner mouseExited:fakeEvent];
2482 void WebViewImpl::sendToolTipMouseEntered()
2484 // Nothing matters except window, trackingNumber, and userData.
2485 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
2486 location:NSMakePoint(0, 0)
2489 windowNumber:m_view.window.windowNumber
2492 trackingNumber:TRACKING_RECT_TAG
2493 userData:m_trackingRectUserData];
2494 [m_trackingRectOwner mouseEntered:fakeEvent];
2497 NSString *WebViewImpl::stringForToolTip(NSToolTipTag tag)
2499 return nsStringFromWebCoreString(m_page->toolTip());
2502 void WebViewImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip)
2504 if (!oldToolTip.isNull())
2505 sendToolTipMouseExited();
2507 if (!newToolTip.isEmpty()) {
2508 // See radar 3500217 for why we remove all tooltips rather than just the single one we created.
2509 [m_view removeAllToolTips];
2510 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000);
2511 m_lastToolTipTag = [m_view addToolTipRect:wideOpenRect owner:m_view userData:NULL];
2512 sendToolTipMouseEntered();
2516 void WebViewImpl::setAcceleratedCompositingRootLayer(CALayer *rootLayer)
2518 [rootLayer web_disableAllActions];
2520 m_rootLayer = rootLayer;
2523 if (m_thumbnailView) {
2524 updateThumbnailViewLayer();
2529 [CATransaction begin];
2530 [CATransaction setDisableActions:YES];
2533 if (!m_layerHostingView) {
2534 // Create an NSView that will host our layer tree.
2535 m_layerHostingView = adoptNS([[WKFlippedView alloc] initWithFrame:m_view.bounds]);
2536 [m_layerHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
2538 [m_view addSubview:m_layerHostingView.get() positioned:NSWindowBelow relativeTo:nil];
2540 // Create a root layer that will back the NSView.
2541 RetainPtr<CALayer> layer = adoptNS([[CALayer alloc] init]);
2542 [layer setDelegate:[WebActionDisablingCALayerDelegate shared]];
2544 [layer setName:@"Hosting root layer"];
2547 [m_layerHostingView setLayer:layer.get()];
2548 [m_layerHostingView setWantsLayer:YES];
2551 [m_layerHostingView layer].sublayers = [NSArray arrayWithObject:rootLayer];
2552 } else if (m_layerHostingView) {
2553 [m_layerHostingView removeFromSuperview];
2554 [m_layerHostingView setLayer:nil];
2555 [m_layerHostingView setWantsLayer:NO];
2557 m_layerHostingView = nullptr;
2560 [CATransaction commit];
2564 void WebViewImpl::setThumbnailView(_WKThumbnailView *thumbnailView)
2566 ASSERT(!m_thumbnailView || !thumbnailView);
2568 m_thumbnailView = thumbnailView;
2571 updateThumbnailViewLayer();
2573 setAcceleratedCompositingRootLayer(m_rootLayer.get());
2576 void WebViewImpl::reparentLayerTreeInThumbnailView()
2578 m_thumbnailView._thumbnailLayer = m_rootLayer.get();
2581 void WebViewImpl::updateThumbnailViewLayer()
2583 _WKThumbnailView *thumbnailView = m_thumbnailView;
2584 ASSERT(thumbnailView);
2586 if (thumbnailView._waitingForSnapshot && m_view.window)
2587 reparentLayerTreeInThumbnailView();
2590 void WebViewImpl::setInspectorAttachmentView(NSView *newView)
2592 NSView *oldView = m_inspectorAttachmentView.get();
2593 if (oldView == newView)
2596 m_inspectorAttachmentView = newView;
2597 m_page->inspector()->attachmentViewDidChange(oldView ? oldView : m_view, newView ? newView : m_view);
2600 NSView *WebViewImpl::inspectorAttachmentView()
2602 NSView *attachmentView = m_inspectorAttachmentView.get();
2603 return attachmentView ? attachmentView : m_view;
2606 _WKRemoteObjectRegistry *WebViewImpl::remoteObjectRegistry()
2608 if (!m_remoteObjectRegistry) {
2609 m_remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:m_page]);
2610 m_page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), m_page->pageID(), [m_remoteObjectRegistry remoteObjectRegistry]);
2613 return m_remoteObjectRegistry.get();
2616 WKBrowsingContextController *WebViewImpl::browsingContextController()
2618 if (!m_browsingContextController)
2619 m_browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(m_page.ptr())]);
2621 return m_browsingContextController.get();
2623 #endif // WK_API_ENABLED
2625 #if ENABLE(DRAG_SUPPORT)
2626 void WebViewImpl::draggedImage(NSImage *image, CGPoint endPoint, NSDragOperation operation)
2628 #pragma clang diagnostic push
2629 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2630 NSPoint windowImageLoc = [m_view.window convertScreenToBase:NSPointFromCGPoint(endPoint)];
2631 #pragma clang diagnostic pop
2632 NSPoint windowMouseLoc = windowImageLoc;
2634 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event.
2635 m_ignoresMouseDraggedEvents = true;
2637 m_page->dragEnded(WebCore::IntPoint(windowMouseLoc), WebCore::globalPoint(windowMouseLoc, m_view.window), operation);
2640 static WebCore::DragApplicationFlags applicationFlagsForDrag(NSView *view, id <NSDraggingInfo> draggingInfo)
2643 if ([NSApp modalWindow])
2644 flags = WebCore::DragApplicationIsModal;
2645 if (view.window.attachedSheet)
2646 flags |= WebCore::DragApplicationHasAttachedSheet;
2647 if (draggingInfo.draggingSource == view)
2648 flags |= WebCore::DragApplicationIsSource;
2649 if ([NSApp currentEvent].modifierFlags & NSAlternateKeyMask)
2650 flags |= WebCore::DragApplicationIsCopyKeyDown;
2651 return static_cast<WebCore::DragApplicationFlags>(flags);
2655 NSDragOperation WebViewImpl::draggingEntered(id <NSDraggingInfo> draggingInfo)
2657 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2658 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2659 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2661 m_page->resetCurrentDragInformation();
2662 m_page->dragEntered(dragData, draggingInfo.draggingPasteboard.name);
2663 return NSDragOperationCopy;
2666 NSDragOperation WebViewImpl::draggingUpdated(id <NSDraggingInfo> draggingInfo)
2668 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2669 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2670 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2671 m_page->dragUpdated(dragData, draggingInfo.draggingPasteboard.name);
2673 NSInteger numberOfValidItemsForDrop = m_page->currentDragNumberOfFilesToBeAccepted();
2674 NSDraggingFormation draggingFormation = NSDraggingFormationNone;
2675 if (m_page->currentDragIsOverFileInput() && numberOfValidItemsForDrop > 0)
2676 draggingFormation = NSDraggingFormationList;
2678 if (draggingInfo.numberOfValidItemsForDrop != numberOfValidItemsForDrop)
2679 [draggingInfo setNumberOfValidItemsForDrop:numberOfValidItemsForDrop];
2680 if (draggingInfo.draggingFormation != draggingFormation)
2681 [draggingInfo setDraggingFormation:draggingFormation];
2683 return m_page->currentDragOperation();
2686 void WebViewImpl::draggingExited(id <NSDraggingInfo> draggingInfo)
2688 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2689 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2690 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2691 m_page->dragExited(dragData, draggingInfo.draggingPasteboard.name);
2692 m_page->resetCurrentDragInformation();
2695 bool WebViewImpl::prepareForDragOperation(id <NSDraggingInfo>)
2700 // FIXME: This code is more or less copied from Pasteboard::getBestURL.
2701 // It would be nice to be able to share the code somehow.
2702 static bool maybeCreateSandboxExtensionFromPasteboard(NSPasteboard *pasteboard, SandboxExtension::Handle& sandboxExtensionHandle)
2704 NSArray *types = pasteboard.types;
2705 if (![types containsObject:NSFilenamesPboardType])
2708 NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
2709 if (files.count != 1)
2712 NSString *file = [files objectAtIndex:0];
2714 if (![[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory])
2720 SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle);
2724 static void createSandboxExtensionsForFileUpload(NSPasteboard *pasteboard, SandboxExtension::HandleArray& handles)
2726 NSArray *types = pasteboard.types;
2727 if (![types containsObject:NSFilenamesPboardType])
2730 NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
2731 handles.allocate(files.count);
2732 for (unsigned i = 0; i < files.count; i++) {
2733 NSString *file = [files objectAtIndex:i];
2734 if (![[NSFileManager defaultManager] fileExistsAtPath:file])
2736 SandboxExtension::Handle handle;
2737 SandboxExtension::createHandle(file, SandboxExtension::ReadOnly, handles[i]);
2741 bool WebViewImpl::performDragOperation(id <NSDraggingInfo> draggingInfo)
2743 WebCore::IntPoint client([m_view convertPoint:draggingInfo.draggingLocation fromView:nil]);
2744 WebCore::IntPoint global(WebCore::globalPoint(draggingInfo.draggingLocation, m_view.window));
2745 WebCore::DragData dragData(draggingInfo, client, global, static_cast<WebCore::DragOperation>(draggingInfo.draggingSourceOperationMask), applicationFlagsForDrag(m_view, draggingInfo));
2747 SandboxExtension::Handle sandboxExtensionHandle;
2748 bool createdExtension = maybeCreateSandboxExtensionFromPasteboard(draggingInfo.draggingPasteboard, sandboxExtensionHandle);
2749 if (createdExtension)
2750 m_page->process().willAcquireUniversalFileReadSandboxExtension();
2752 SandboxExtension::HandleArray sandboxExtensionForUpload;
2753 createSandboxExtensionsForFileUpload(draggingInfo.draggingPasteboard, sandboxExtensionForUpload);
2755 m_page->performDragOperation(dragData, draggingInfo.draggingPasteboard.name, sandboxExtensionHandle, sandboxExtensionForUpload);
2760 NSView *WebViewImpl::hitTestForDragTypes(CGPoint point, NSSet *types)
2762 // This code is needed to support drag and drop when the drag types cannot be matched.
2763 // This is the case for elements that do not place content
2764 // in the drag pasteboard automatically when the drag start (i.e. dragging a DIV element).
2765 if ([[m_view superview] mouse:NSPointFromCGPoint(point) inRect:[m_view frame]])
2770 void WebViewImpl::registerDraggedTypes()
2772 auto types = adoptNS([[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()]);
2773 [types addObjectsFromArray:PasteboardTypes::forURL()];
2774 [m_view registerForDraggedTypes:[types allObjects]];
2776 #endif // ENABLE(DRAG_SUPPORT)
2778 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
2779 void WebViewImpl::startWindowDrag()
2781 [m_view.window performWindowDragWithEvent:m_lastMouseDownEvent.get()];
2785 void WebViewImpl::dragImageForView(NSView *view, NSImage *image, CGPoint clientPoint, bool linkDrag)
2787 // The call below could release the view.
2788 RetainPtr<NSView> protector(m_view);
2790 #pragma clang diagnostic push
2791 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2792 [view dragImage:image
2793 at:NSPointFromCGPoint(clientPoint)
2795 event:linkDrag ? [NSApp currentEvent] : m_lastMouseDownEvent.get()
2796 pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
2799 #pragma clang diagnostic pop
2802 static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension)
2804 NSString *extensionAsSuffix = [@"." stringByAppendingString:extension];
2805 return hasCaseInsensitiveSuffix(filename, extensionAsSuffix) || (stringIsCaseInsensitiveEqualToString(extension, @"jpeg")
2806 && hasCaseInsensitiveSuffix(filename, @".jpg"));
2809 void WebViewImpl::setFileAndURLTypes(NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, NSPasteboard *pasteboard)
2811 if (!matchesExtensionOrEquivalent(filename, extension))
2812 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:extension];
2814 [pasteboard setString:visibleURL forType:NSStringPboardType];
2815 [pasteboard setString:visibleURL forType:PasteboardTypes::WebURLPboardType];
2816 [pasteboard setString:title forType:PasteboardTypes::WebURLNamePboardType];
2817 [pasteboard setPropertyList:[NSArray arrayWithObjects:[NSArray arrayWithObject:visibleURL], [NSArray arrayWithObject:title], nil] forType:PasteboardTypes::WebURLsWithTitlesPboardType];
2818 [pasteboard setPropertyList:[NSArray arrayWithObject:extension] forType:NSFilesPromisePboardType];
2819 m_promisedFilename = filename;
2820 m_promisedURL = url;
2823 void WebViewImpl::setPromisedDataForImage(WebCore::Image* image, NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, WebCore::SharedBuffer* archiveBuffer, NSString *pasteboardName)
2825 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
2826 RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
2828 [types addObjectsFromArray:archiveBuffer ? PasteboardTypes::forImagesWithArchive() : PasteboardTypes::forImages()];
2829 [pasteboard declareTypes:types.get() owner:m_view];
2830 setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
2833 [pasteboard setData:archiveBuffer->createNSData().get() forType:PasteboardTypes::WebArchivePboardType];
2835 m_promisedImage = image;
2838 #if ENABLE(ATTACHMENT_ELEMENT)
2839 void WebViewImpl::setPromisedDataForAttachment(NSString *filename, NSString *extension, NSString *title, NSString *url, NSString *visibleURL, NSString *pasteboardName)
2841 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName];
2842 RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]);
2843 [types addObjectsFromArray:PasteboardTypes::forURL()];
2844 [pasteboard declareTypes:types.get() owner:m_view];
2845 setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
2847 RetainPtr<NSMutableArray> paths = adoptNS([[NSMutableArray alloc] init]);
2848 [paths addObject:title];
2849 [pasteboard setPropertyList:paths.get() forType:NSFilenamesPboardType];
2851 m_promisedImage = nullptr;
2855 void WebViewImpl::pasteboardChangedOwner(NSPasteboard *pasteboard)
2857 m_promisedImage = nullptr;
2858 m_promisedFilename = "";
2862 void WebViewImpl::provideDataForPasteboard(NSPasteboard *pasteboard, NSString *type)
2864 // FIXME: need to support NSRTFDPboardType
2866 if ([type isEqual:NSTIFFPboardType] && m_promisedImage) {
2867 [pasteboard setData:(NSData *)m_promisedImage->getTIFFRepresentation() forType:NSTIFFPboardType];
2868 m_promisedImage = nullptr;
2872 static BOOL fileExists(NSString *path)
2874 struct stat statBuffer;
2875 return !lstat([path fileSystemRepresentation], &statBuffer);
2878 static NSString *pathWithUniqueFilenameForPath(NSString *path)
2880 // "Fix" the filename of the path.
2881 NSString *filename = filenameByFixingIllegalCharacters([path lastPathComponent]);
2882 path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename];
2884 if (fileExists(path)) {
2885 // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename.
2886 NSString *extensions = nil;
2887 NSString *pathWithoutExtensions;
2888 NSString *lastPathComponent = [path lastPathComponent];
2889 NSRange periodRange = [lastPathComponent rangeOfString:@"."];
2891 if (periodRange.location == NSNotFound) {
2892 pathWithoutExtensions = path;
2894 extensions = [lastPathComponent substringFromIndex:periodRange.location + 1];
2895 lastPathComponent = [lastPathComponent substringToIndex:periodRange.location];
2896 pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent];
2899 for (unsigned i = 1; ; i++) {
2900 NSString *pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i];
2901 path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber;
2902 if (!fileExists(path))
2910 NSArray *WebViewImpl::namesOfPromisedFilesDroppedAtDestination(NSURL *dropDestination)
2912 RetainPtr<NSFileWrapper> wrapper;
2913 RetainPtr<NSData> data;
2915 if (m_promisedImage) {
2916 data = m_promisedImage->data()->createNSData();
2917 wrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data.get()]);
2919 wrapper = adoptNS([[NSFileWrapper alloc] initWithURL:[NSURL URLWithString:m_promisedURL] options:NSFileWrapperReadingImmediate error:nil]);
2922 [wrapper setPreferredFilename:m_promisedFilename];
2924 LOG_ERROR("Failed to create image file.");
2928 // FIXME: Report an error if we fail to create a file.
2929 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]];
2930 path = pathWithUniqueFilenameForPath(path);
2931 if (![wrapper writeToURL:[NSURL fileURLWithPath:path] options:NSFileWrapperWritingWithNameUpdating originalContentsURL:nil error:nullptr])
2932 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToURL:options:originalContentsURL:error:]");
2934 if (!m_promisedURL.isEmpty())
2935 WebCore::setMetadataURL(m_promisedURL, "", String(path));
2937 return [NSArray arrayWithObject:[path lastPathComponent]];
2940 static RetainPtr<CGImageRef> takeWindowSnapshot(CGSWindowID windowID, bool captureAtNominalResolution)
2942 CGSWindowCaptureOptions options = kCGSCaptureIgnoreGlobalClipShape;
2943 if (captureAtNominalResolution)
2944 options |= kCGSWindowCaptureNominalResolution;
2945 RetainPtr<CFArrayRef> windowSnapshotImages = adoptCF(CGSHWCaptureWindowList(CGSMainConnectionID(), &windowID, 1, options));
2947 if (windowSnapshotImages && CFArrayGetCount(windowSnapshotImages.get()))
2948 return (CGImageRef)CFArrayGetValueAtIndex(windowSnapshotImages.get(), 0);
2950 // Fall back to the non-hardware capture path if we didn't get a snapshot
2951 // (which usually happens if the window is fully off-screen).
2952 CGWindowImageOption imageOptions = kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque;
2953 if (captureAtNominalResolution)
2954 imageOptions |= kCGWindowImageNominalResolution;
2955 return adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, imageOptions));
2958 RefPtr<ViewSnapshot> WebViewImpl::takeViewSnapshot()
2960 NSWindow *window = m_view.window;
2962 CGSWindowID windowID = (CGSWindowID)window.windowNumber;
2963 if (!windowID || !window.isVisible)
2966 RetainPtr<CGImageRef> windowSnapshotImage = takeWindowSnapshot(windowID, false);
2967 if (!windowSnapshotImage)
2970 // Work around <rdar://problem/17084993>; re-request the snapshot at kCGWindowImageNominalResolution if it was captured at the wrong scale.
2971 CGFloat desiredSnapshotWidth = window.frame.size.width * window.screen.backingScaleFactor;
2972 if (CGImageGetWidth(windowSnapshotImage.get()) != desiredSnapshotWidth)
2973 windowSnapshotImage = takeWindowSnapshot(windowID, true);
2975 if (!windowSnapshotImage)
2978 ViewGestureController& gestureController = ensureGestureController();
2980 NSRect windowCaptureRect;
2981 WebCore::FloatRect boundsForCustomSwipeViews = gestureController.windowRelativeBoundsForCustomSwipeViews();
2982 if (!boundsForCustomSwipeViews.isEmpty())
2983 windowCaptureRect = boundsForCustomSwipeViews;
2985 NSRect unobscuredBounds = m_view.bounds;
2986 float topContentInset = m_page->topContentInset();
2987 unobscuredBounds.origin.y += topContentInset;
2988 unobscuredBounds.size.height -= topContentInset;
2989 windowCaptureRect = [m_view convertRect:unobscuredBounds toView:nil];
2992 NSRect windowCaptureScreenRect = [window convertRectToScreen:windowCaptureRect];
2993 CGRect windowScreenRect;
2994 CGSGetScreenRectForWindow(CGSMainConnectionID(), (CGSWindowID)[window windowNumber], &windowScreenRect);
2996 NSRect croppedImageRect = windowCaptureRect;
2997 croppedImageRect.origin.y = windowScreenRect.size.height - windowCaptureScreenRect.size.height - NSMinY(windowCaptureRect);
2999 auto croppedSnapshotImage = adoptCF(CGImageCreateWithImageInRect(windowSnapshotImage.get(), NSRectToCGRect([window convertRectToBacking:croppedImageRect])));
3001 auto surface = WebCore::IOSurface::createFromImage(croppedSnapshotImage.get());
3004 surface->setIsVolatile(true);
3006 return ViewSnapshot::create(WTF::move(surface));
3009 void WebViewImpl::saveBackForwardSnapshotForCurrentItem()
3011 m_page->recordNavigationSnapshot();
3014 void WebViewImpl::saveBackForwardSnapshotForItem(WebBackForwardListItem& item)
3016 m_page->recordNavigationSnapshot(item);
3019 ViewGestureController& WebViewImpl::ensureGestureController()
3021 if (!m_gestureController)
3022 m_gestureController = std::make_unique<ViewGestureController>(m_page);
3023 return *m_gestureController;
3026 void WebViewImpl::setAllowsBackForwardNavigationGestures(bool allowsBackForwardNavigationGestures)
3028 m_allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
3029 m_page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
3030 m_page->setShouldUseImplicitRubberBandControl(allowsBackForwardNavigationGestures);
3033 void WebViewImpl::setAllowsMagnification(bool allowsMagnification)
3035 m_allowsMagnification = allowsMagnification;
3038 void WebViewImpl::setMagnification(double magnification, CGPoint centerPoint)
3040 if (magnification <= 0 || isnan(magnification) || isinf(magnification))
3041 [NSException raise:NSInvalidArgumentException format:@"Magnification should be a positive number"];
3043 dismissContentRelativeChildWindowsWithAnimation(false);
3045 m_page->scalePageInViewCoordinates(magnification, WebCore::roundedIntPoint(centerPoint));
3048 void WebViewImpl::setMagnification(double magnification)
3050 if (magnification <= 0 || isnan(magnification) || isinf(magnification))
3051 [NSException raise:NSInvalidArgumentException format:@"Magnification should be a positive number"];
3053 dismissContentRelativeChildWindowsWithAnimation(false);
3055 WebCore::FloatPoint viewCenter(NSMidX([m_view bounds]), NSMidY([m_view bounds]));
3056 m_page->scalePageInViewCoordinates(magnification, roundedIntPoint(viewCenter));
3059 double WebViewImpl::magnification() const
3061 if (m_gestureController)
3062 return m_gestureController->magnification();
3063 return m_page->pageScaleFactor();
3066 void WebViewImpl::setCustomSwipeViews(NSArray *customSwipeViews)
3068 if (!customSwipeViews.count && !m_gestureController)
3071 Vector<RetainPtr<NSView>> views;
3072 views.reserveInitialCapacity(customSwipeViews.count);
3073 for (NSView *view in customSwipeViews)
3074 views.uncheckedAppend(view);
3076 ensureGestureController().setCustomSwipeViews(views);
3079 void WebViewImpl::setCustomSwipeViewsTopContentInset(float topContentInset)
3081 ensureGestureController().setCustomSwipeViewsTopContentInset(topContentInset);
3084 bool WebViewImpl::tryToSwipeWithEvent(NSEvent *event, bool ignoringPinnedState)
3086 if (!m_allowsBackForwardNavigationGestures)
3089 auto& gestureController = ensureGestureController();
3091 bool wasIgnoringPinnedState = gestureController.shouldIgnorePinnedState();
3092 gestureController.setShouldIgnorePinnedState(ignoringPinnedState);
3094 bool handledEvent = gestureController.handleScrollWheelEvent(event);
3096 gestureController.setShouldIgnorePinnedState(wasIgnoringPinnedState);
3098 return handledEvent;
3101 void WebViewImpl::setDidMoveSwipeSnapshotCallback(void(^callback)(CGRect))
3103 if (!m_allowsBackForwardNavigationGestures)
3106 ensureGestureController().setDidMoveSwipeSnapshotCallback(callback);
3109 void WebViewImpl::scrollWheel(NSEvent *event)
3111 if (m_ignoresAllEvents)
3114 if (event.phase == NSEventPhaseBegan)
3115 dismissContentRelativeChildWindowsWithAnimation(false);
3117 if (m_allowsBackForwardNavigationGestures && ensureGestureController().handleScrollWheelEvent(event))
3120 NativeWebWheelEvent webEvent = NativeWebWheelEvent(event, m_view);
3121 m_page->handleWheelEvent(webEvent);
3124 void WebViewImpl::swipeWithEvent(NSEvent *event)
3126 if (m_ignoresNonWheelEvents)
3129 if (!m_allowsBackForwardNavigationGestures) {
3130 [m_view _web_superSwipeWithEvent:event];
3134 if (event.deltaX > 0.0)
3136 else if (event.deltaX < 0.0)
3137 m_page->goForward();
3139 [m_view _web_superSwipeWithEvent:event];
3142 void WebViewImpl::magnifyWithEvent(NSEvent *event)
3144 if (!m_allowsMagnification) {
3145 #if ENABLE(MAC_GESTURE_EVENTS)
3146 NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
3147 m_page->handleGestureEvent(webEvent);
3149 [m_view _web_superMagnifyWithEvent:event];
3153 dismissContentRelativeChildWindowsWithAnimation(false);
3155 auto& gestureController = ensureGestureController();
3157 #if ENABLE(MAC_GESTURE_EVENTS)
3158 if (gestureController.hasActiveMagnificationGesture()) {
3159 gestureController.handleMagnificationGestureEvent(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
3163 NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
3164 m_page->handleGestureEvent(webEvent);
3166 gestureController.handleMagnificationGestureEvent(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
3170 void WebViewImpl::smartMagnifyWithEvent(NSEvent *event)
3172 if (!m_allowsMagnification) {
3173 [m_view _web_superSmartMagnifyWithEvent:event];
3177 dismissContentRelativeChildWindowsWithAnimation(false);
3179 ensureGestureController().handleSmartMagnificationGesture([m_view convertPoint:event.locationInWindow fromView:nil]);
3182 void WebViewImpl::setLastMouseDownEvent(NSEvent *event)
3184 ASSERT(!event || event.type == NSLeftMouseDown || event.type == NSRightMouseDown || event.type == NSOtherMouseDown);
3186 if (event == m_lastMouseDownEvent.get())
3189 m_lastMouseDownEvent = event;
3192 #if ENABLE(MAC_GESTURE_EVENTS)
3193 void WebViewImpl::rotateWithEvent(NSEvent *event)
3195 NativeWebGestureEvent webEvent = NativeWebGestureEvent(event, m_view);
3196 m_page->handleGestureEvent(webEvent);
3200 void WebViewImpl::gestureEventWasNotHandledByWebCore(NSEvent *event)
3202 [m_view _web_gestureEventWasNotHandledByWebCore:event];
3205 void WebViewImpl::gestureEventWasNotHandledByWebCoreFromViewOnly(NSEvent *event)
3207 #if ENABLE(MAC_GESTURE_EVENTS)
3208 if (m_gestureController)
3209 m_gestureController->gestureEventWasNotHandledByWebCore(event, [m_view convertPoint:event.locationInWindow fromView:nil]);
3213 void WebViewImpl::doneWithKeyEvent(NSEvent *event, bool eventWasHandled)
3215 if ([event type] != NSKeyDown)
3218 if (tryPostProcessPluginComplexTextInputKeyDown(event))
3221 if (eventWasHandled) {
3222 [NSCursor setHiddenUntilMouseMoves:YES];
3226 // resending the event may destroy this WKView
3227 RetainPtr<NSView> protector(m_view);
3229 ASSERT(!m_keyDownEventBeingResent);
3230 m_keyDownEventBeingResent = event;
3231 [NSApp _setCurrentEvent:event];
3232 [NSApp sendEvent:event];
3234 m_keyDownEventBeingResent = nullptr;
3237 NSArray *WebViewImpl::validAttributesForMarkedText()
3239 static NSArray *validAttributes;
3240 if (!validAttributes) {
3241 validAttributes = @[ NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, NSMarkedClauseSegmentAttributeName,
3242 #if USE(DICTATION_ALTERNATIVES)
3243 NSTextAlternativesAttributeName,
3245 #if USE(INSERTION_UNDO_GROUPING)
3246 NSTextInsertionUndoableAttributeName,
3249 // NSText also supports the following attributes, but it's
3250 // hard to tell which are really required for text input to
3251 // work well; I have not seen any input method make use of them yet.
3252 // NSFontAttributeName, NSForegroundColorAttributeName,
3253 // NSBackgroundColorAttributeName, NSLanguageAttributeName.
3254 CFRetain(validAttributes);
3256 LOG(TextInput, "validAttributesForMarkedText -> (...)");
3257 return validAttributes;
3260 static Vector<WebCore::CompositionUnderline> extractUnderlines(NSAttributedString *string)
3262 Vector<WebCore::CompositionUnderline> result;
3263 int length = string.string.length;
3265 for (int i = 0; i < length;) {
3267 NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)];
3269 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
3270 WebCore::Color color = WebCore::Color::black;
3271 if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName])
3272 color = WebCore::colorFromNSColor(colorAttr);
3273 result.append(WebCore::CompositionUnderline(range.location, NSMaxRange(range), color, style.intValue > 1));
3276 i = range.location + range.length;
3282 static bool eventKeyCodeIsZeroOrNumLockOrFn(NSEvent *event)
3284 unsigned short keyCode = [event keyCode];
3285 return !keyCode || keyCode == 10 || keyCode == 63;
3288 #if USE(ASYNC_NSTEXTINPUTCLIENT)
3290 Vector<WebCore::KeypressCommand> WebViewImpl::collectKeyboardLayoutCommandsForEvent(NSEvent *event)
3292 Vector<WebCore::KeypressCommand> commands;
3294 if ([event type] != NSKeyDown)
3297 ASSERT(!m_collectedKeypressCommands);
3298 m_collectedKeypressCommands = &commands;
3300 if (NSTextInputContext *context = inputContext())
3301 [context handleEventByKeyboardLayout:event];
3303 [m_view interpretKeyEvents:[NSArray arrayWithObject:event]];
3305 m_collectedKeypressCommands = nullptr;
3310 void WebViewImpl::interpretKeyEvent(NSEvent *event, void(^completionHandler)(BOOL handled, const Vector<WebCore::KeypressCommand>& commands))
3312 // For regular Web content, input methods run before passing a keydown to DOM, but plug-ins get an opportunity to handle the event first.
3313 // There is no need to collect commands, as the plug-in cannot execute them.
3314 if (pluginComplexTextInputIdentifier()) {
3315 completionHandler(NO, { });
3319 if (!inputContext()) {
3320 auto commands = collectKeyboardLayoutCommandsForEvent(event);
3321 completionHandler(NO, commands);
3325 LOG(TextInput, "-> handleEventByInputMethod:%p %@", event, event);
3326 [inputContext() handleEventByInputMethod:event completionHandler:^(BOOL handled) {
3328 LOG(TextInput, "... handleEventByInputMethod%s handled", handled ? "" : " not");
3330 completionHandler(YES, { });
3334 auto commands = collectKeyboardLayoutCommandsForEvent(event);
3335 completionHandler(NO, commands);
3339 void WebViewImpl::doCommandBySelector(SEL selector)
3341 LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector));
3343 if (auto* keypressCommands = m_collectedKeypressCommands) {
3344 WebCore::KeypressCommand command(NSStringFromSelector(selector));
3345 keypressCommands->append(command);
3346 LOG(TextInput, "...stored");
3347 m_page->registerKeypressCommandName(command.commandName);
3349 // FIXME: Send the command to Editor synchronously and only send it along the
3350 // responder chain if it's a selector that does not correspond to an editing command.
3351 [m_view _web_superDoCommandBySelector:selector];
3355 void WebViewImpl::insertText(id string)
3357 // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context,
3358 // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText
3359 // command ensures that a keypress event is dispatched as appropriate.
3360 insertText(string, NSMakeRange(NSNotFound, 0));
3363 void WebViewImpl::insertText(id string, NSRange replacementRange)
3365 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
3366 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]);
3368 if (replacementRange.location != NSNotFound)
3369 LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length);
3371 LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string);
3374 Vector<WebCore::TextAlternativeWithRange> dictationAlternatives;
3376 bool registerUndoGroup = false;
3377 if (isAttributedString) {
3378 #if USE(DICTATION_ALTERNATIVES)
3379 WebCore::collectDictationTextAlternatives(string, dictationAlternatives);
3381 #if USE(INSERTION_UNDO_GROUPING)
3382 registerUndoGroup = WebCore::shouldRegisterInsertionUndoGroup(string);
3384 // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data.
3385 text = [string string];
3389 // insertText can be called for several reasons:
3390 // - If it's from normal key event processing (including key bindings), we save the action to perform it later.
3391 // - If it's from an input method, then we should insert the text now.
3392 // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse),
3393 // then we also execute it immediately, as there will be no other chance.
3394 Vector<WebCore::KeypressCommand>* keypressCommands = m_collectedKeypressCommands;
3395 if (keypressCommands) {
3396 ASSERT(replacementRange.location == NSNotFound);
3397 WebCore::KeypressCommand command("insertText:", text);
3398 keypressCommands->append(command);
3399 LOG(TextInput, "...stored");
3400 m_page->registerKeypressCommandName(command.commandName);
3404 String eventText = text;
3405 eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore
3406 if (!dictationAlternatives.isEmpty())
3407 m_page->insertDictatedTextAsync(eventText, replacementRange, dictationAlternatives, registerUndoGroup);
3409 m_page->insertTextAsync(eventText, replacementRange, registerUndoGroup);
3412 void WebViewImpl::selectedRangeWithCompletionHandler(void(^completionHandlerPtr)(NSRange selectedRange))
3414 auto completionHandler = adoptNS([completionHandlerPtr copy]);
3416 LOG(TextInput, "selectedRange");
3417 m_page->getSelectedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
3418 void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get();
3419 if (error != WebKit::CallbackBase::Error::None) {
3420 LOG(TextInput, " ...selectedRange failed.");
3421 completionHandlerBlock(NSMakeRange(NSNotFound, 0));
3424 NSRange result = editingRangeResult;
3425 if (result.location == NSNotFound)
3426 LOG(TextInput, " -> selectedRange returned (NSNotFound, %llu)", result.length);
3428 LOG(TextInput, " -> selectedRange returned (%llu, %llu)", result.location, result.length);
3429 completionHandlerBlock(result);
3433 void WebViewImpl::markedRangeWithCompletionHandler(void(^completionHandlerPtr)(NSRange markedRange))
3435 auto completionHandler = adoptNS([completionHandlerPtr copy]);
3437 LOG(TextInput, "markedRange");
3438 m_page->getMarkedRangeAsync([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, " ...markedRange failed.");
3442 completionHandlerBlock(NSMakeRange(NSNotFound, 0));
3445 NSRange result = editingRangeResult;
3446 if (result.location == NSNotFound)
3447 LOG(TextInput, " -> markedRange returned (NSNotFound, %llu)", result.length);
3449 LOG(TextInput, " -> markedRange returned (%llu, %llu)", result.location, result.length);
3450 completionHandlerBlock(result);
3454 void WebViewImpl::hasMarkedTextWithCompletionHandler(void(^completionHandlerPtr)(BOOL hasMarkedText))
3456 auto completionHandler = adoptNS([completionHandlerPtr copy]);
3458 LOG(TextInput, "hasMarkedText");
3459 m_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, WebKit::CallbackBase::Error error) {
3460 void (^completionHandlerBlock)(BOOL) = (void (^)(BOOL))completionHandler.get();
3461 if (error != WebKit::CallbackBase::Error::None) {
3462 LOG(TextInput, " ...hasMarkedText failed.");
3463 completionHandlerBlock(NO);
3466 BOOL hasMarkedText = editingRangeResult.location != notFound;
3467 LOG(TextInput, " -> hasMarkedText returned %u", hasMarkedText);
3468 completionHandlerBlock(hasMarkedText);
3472 void WebViewImpl::attributedSubstringForProposedRange(NSRange proposedRange, void(^completionHandlerPtr)(NSAttributedString *attrString, NSRange actualRange))
3474 auto completionHandler = adoptNS([completionHandlerPtr copy]);
3476 LOG(TextInput, "attributedSubstringFromRange:(%u, %u)", proposedRange.location, proposedRange.length);
3477 m_page->attributedSubstringForCharacterRangeAsync(proposedRange, [completionHandler](const AttributedString& string, const EditingRange& actualRange, WebKit::CallbackBase::Error error) {
3478 void (^completionHandlerBlock)(NSAttributedString *, NSRange) = (void (^)(NSAttributedString *, NSRange))completionHandler.get();
3479 if (error != WebKit::CallbackBase::Error::None) {
3480 LOG(TextInput, " ...attributedSubstringFromRange failed.");
3481 completionHandlerBlock(0, NSMakeRange(NSNotFound, 0));
3484 LOG(TextInput, " -> attributedSubstringFromRange returned %@", [string.string.get() string]);
3485 completionHandlerBlock([[string.string.get() retain] autorelease], actualRange);
3489 void WebViewImpl::firstRectForCharacterRange(NSRange range, void(^completionHandlerPtr)(NSRect firstRect, NSRange actualRange))
3491 auto completionHandler = adoptNS([completionHandlerPtr copy]);
3493 LOG(TextInput, "firstRectForCharacterRange:(%u, %u)", range.location, range.length);
3495 // Just to match NSTextView's behavior. Regression tests cannot detect this;
3496 // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682
3497 // (type something; try ranges (1, -1) and (2, -1).
3498 if ((range.location + range.length < range.location) && (range.location + range.length != 0))
3501 if (range.location == NSNotFound) {
3502 LOG(TextInput, " -> NSZeroRect");