/* * Copyright (C) 2006-2020 Apple Inc. All rights reserved. * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "WebChromeClient.h" #import "DOMElementInternal.h" #import "DOMHTMLInputElementInternal.h" #import "DOMNodeInternal.h" #import "PopupMenuMac.h" #import "SearchPopupMenuMac.h" #import "WebBasePluginPackage.h" #import "WebDefaultUIDelegate.h" #import "WebDelegateImplementationCaching.h" #import "WebElementDictionary.h" #import "WebFormDelegate.h" #import "WebFrameInternal.h" #import "WebFrameView.h" #import "WebHTMLViewInternal.h" #import "WebHistoryInternal.h" #import "WebKitFullScreenListener.h" #import "WebKitPrefix.h" #import "WebNSURLRequestExtras.h" #import "WebOpenPanelResultListener.h" #import "WebPlugin.h" #import "WebQuotaManager.h" #import "WebSecurityOriginInternal.h" #import "WebSelectionServiceController.h" #import "WebUIDelegatePrivate.h" #import "WebView.h" #import "WebViewInternal.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #if HAVE(WEBGPU_IMPLEMENTATION) #import #endif #if PLATFORM(IOS_FAMILY) && ENABLE(GEOLOCATION) #import #endif #if ENABLE(POINTER_LOCK) #import #endif #if PLATFORM(IOS_FAMILY) #import #import #import #endif NSString *WebConsoleMessageXMLMessageSource = @"XMLMessageSource"; NSString *WebConsoleMessageJSMessageSource = @"JSMessageSource"; NSString *WebConsoleMessageNetworkMessageSource = @"NetworkMessageSource"; NSString *WebConsoleMessageConsoleAPIMessageSource = @"ConsoleAPIMessageSource"; NSString *WebConsoleMessageStorageMessageSource = @"StorageMessageSource"; NSString *WebConsoleMessageAppCacheMessageSource = @"AppCacheMessageSource"; NSString *WebConsoleMessageRenderingMessageSource = @"RenderingMessageSource"; NSString *WebConsoleMessageCSSMessageSource = @"CSSMessageSource"; NSString *WebConsoleMessageSecurityMessageSource = @"SecurityMessageSource"; NSString *WebConsoleMessageContentBlockerMessageSource = @"ContentBlockerMessageSource"; NSString *WebConsoleMessageMediaMessageSource = @"MediaMessageSource"; NSString *WebConsoleMessageMediaSourceMessageSource = @"MediaSourceMessageSource"; NSString *WebConsoleMessageWebRTCMessageSource = @"WebRTCMessageSource"; NSString *WebConsoleMessageITPDebugMessageSource = @"ITPDebugMessageSource"; NSString *WebConsoleMessagePrivateClickMeasurementMessageSource = @"PrivateClickMeasurementMessageSource"; NSString *WebConsoleMessagePaymentRequestMessageSource = @"PaymentRequestMessageSource"; NSString *WebConsoleMessageOtherMessageSource = @"OtherMessageSource"; NSString *WebConsoleMessageDebugMessageLevel = @"DebugMessageLevel"; NSString *WebConsoleMessageLogMessageLevel = @"LogMessageLevel"; NSString *WebConsoleMessageInfoMessageLevel = @"InfoMessageLevel"; NSString *WebConsoleMessageWarningMessageLevel = @"WarningMessageLevel"; NSString *WebConsoleMessageErrorMessageLevel = @"ErrorMessageLevel"; #if !PLATFORM(IOS_FAMILY) @interface NSApplication (WebNSApplicationDetails) - (NSCursor *)_cursorRectCursor; @end #endif // For compatibility with old SPI. @interface NSView (WebOldWebKitPlugInDetails) - (void)setIsSelected:(BOOL)isSelected; @end using namespace WebCore; using namespace HTMLNames; WebChromeClient::WebChromeClient(WebView *webView) : m_webView(webView) { } void WebChromeClient::chromeDestroyed() { delete this; } // These functions scale between window and WebView coordinates because JavaScript/DOM operations // assume that the WebView and the window share the same coordinate system. void WebChromeClient::setWindowRect(const FloatRect& rect) { #if !PLATFORM(IOS_FAMILY) NSRect windowRect = toDeviceSpace(rect, [m_webView window]); [[m_webView _UIDelegateForwarder] webView:m_webView setFrame:windowRect]; #endif } FloatRect WebChromeClient::windowRect() { #if !PLATFORM(IOS_FAMILY) NSRect windowRect = [[m_webView _UIDelegateForwarder] webViewFrame:m_webView]; return toUserSpace(windowRect, [m_webView window]); #else return FloatRect(); #endif } // FIXME: We need to add API for setting and getting this. FloatRect WebChromeClient::pageRect() { return [m_webView frame]; } void WebChromeClient::focus() { [[m_webView _UIDelegateForwarder] webViewFocus:m_webView]; } void WebChromeClient::unfocus() { [[m_webView _UIDelegateForwarder] webViewUnfocus:m_webView]; } bool WebChromeClient::canTakeFocus(FocusDirection) { // There's unfortunately no way to determine if we will become first responder again // once we give it up, so we just have to guess that we won't. return true; } void WebChromeClient::takeFocus(FocusDirection direction) { #if !PLATFORM(IOS_FAMILY) if (direction == FocusDirection::Forward) { // Since we're trying to move focus out of m_webView, and because // m_webView may contain subviews within it, we ask it for the next key // view of the last view in its key view loop. This makes m_webView // behave as if it had no subviews, which is the behavior we want. NSView *lastView = [m_webView _findLastViewInKeyViewLoop]; // avoid triggering assertions if the WebView is the only thing in the key loop if ([m_webView _becomingFirstResponderFromOutside] && m_webView == [lastView nextValidKeyView]) return; [[m_webView window] selectKeyViewFollowingView:lastView]; } else { // avoid triggering assertions if the WebView is the only thing in the key loop if ([m_webView _becomingFirstResponderFromOutside] && m_webView == [m_webView previousValidKeyView]) return; [[m_webView window] selectKeyViewPrecedingView:m_webView]; } #endif } void WebChromeClient::focusedElementChanged(Element* element) { if (!is(element)) return; auto& inputElement = downcast(*element); if (!inputElement.isText()) return; CallFormDelegate(m_webView, @selector(didFocusTextField:inFrame:), kit(&inputElement), kit(inputElement.document().frame())); } void WebChromeClient::focusedFrameChanged(Frame*) { } Page* WebChromeClient::createWindow(Frame& frame, const WindowFeatures& features, const NavigationAction&) { id delegate = [m_webView UIDelegate]; WebView *newWebView; #if ENABLE(FULLSCREEN_API) if (frame.document() && frame.document()->fullscreenManager().currentFullscreenElement()) frame.document()->fullscreenManager().cancelFullscreen(); #endif if ([delegate respondsToSelector:@selector(webView:createWebViewWithRequest:windowFeatures:)]) { auto dictFeatures = adoptNS([[NSMutableDictionary alloc] initWithObjectsAndKeys: @(features.menuBarVisible), @"menuBarVisible", @(features.statusBarVisible), @"statusBarVisible", @(features.toolBarVisible), @"toolBarVisible", @(features.scrollbarsVisible), @"scrollbarsVisible", @(features.resizable), @"resizable", @(features.fullscreen), @"fullscreen", @(features.dialog), @"dialog", nil]); if (features.x) [dictFeatures setObject:@(*features.x) forKey:@"x"]; if (features.y) [dictFeatures setObject:@(*features.y) forKey:@"y"]; if (features.width) [dictFeatures setObject:@(*features.width) forKey:@"width"]; if (features.height) [dictFeatures setObject:@(*features.height) forKey:@"height"]; newWebView = CallUIDelegate(m_webView, @selector(webView:createWebViewWithRequest:windowFeatures:), nil, dictFeatures.get()); } else if (features.dialog && [delegate respondsToSelector:@selector(webView:createWebViewModalDialogWithRequest:)]) newWebView = CallUIDelegate(m_webView, @selector(webView:createWebViewModalDialogWithRequest:), nil); else newWebView = CallUIDelegate(m_webView, @selector(webView:createWebViewWithRequest:), nil); return core(newWebView); } void WebChromeClient::show() { [[m_webView _UIDelegateForwarder] webViewShow:m_webView]; } bool WebChromeClient::canRunModal() { return [[m_webView UIDelegate] respondsToSelector:@selector(webViewRunModal:)]; } void WebChromeClient::runModal() { CallUIDelegate(m_webView, @selector(webViewRunModal:)); } void WebChromeClient::setToolbarsVisible(bool b) { [[m_webView _UIDelegateForwarder] webView:m_webView setToolbarsVisible:b]; } bool WebChromeClient::toolbarsVisible() { return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewAreToolbarsVisible:)); } void WebChromeClient::setStatusbarVisible(bool b) { [[m_webView _UIDelegateForwarder] webView:m_webView setStatusBarVisible:b]; } bool WebChromeClient::statusbarVisible() { return CallUIDelegateReturningBoolean(NO, m_webView, @selector(webViewIsStatusBarVisible:)); } void WebChromeClient::setScrollbarsVisible(bool b) { [[[m_webView mainFrame] frameView] setAllowsScrolling:b]; } bool WebChromeClient::scrollbarsVisible() { return [[[m_webView mainFrame] frameView] allowsScrolling]; } void WebChromeClient::setMenubarVisible(bool) { // The menubar is always visible in Mac OS X. return; } bool WebChromeClient::menubarVisible() { // The menubar is always visible in Mac OS X. return true; } void WebChromeClient::setResizable(bool b) { [[m_webView _UIDelegateForwarder] webView:m_webView setResizable:b]; } inline static NSString *stringForMessageSource(MessageSource source) { switch (source) { case MessageSource::XML: return WebConsoleMessageXMLMessageSource; case MessageSource::JS: return WebConsoleMessageJSMessageSource; case MessageSource::Network: return WebConsoleMessageNetworkMessageSource; case MessageSource::ConsoleAPI: return WebConsoleMessageConsoleAPIMessageSource; case MessageSource::Storage: return WebConsoleMessageStorageMessageSource; case MessageSource::AppCache: return WebConsoleMessageAppCacheMessageSource; case MessageSource::Rendering: return WebConsoleMessageRenderingMessageSource; case MessageSource::CSS: return WebConsoleMessageCSSMessageSource; case MessageSource::Security: return WebConsoleMessageSecurityMessageSource; case MessageSource::ContentBlocker: return WebConsoleMessageContentBlockerMessageSource; case MessageSource::Media: return WebConsoleMessageMediaMessageSource; case MessageSource::MediaSource: return WebConsoleMessageMediaSourceMessageSource; case MessageSource::WebRTC: return WebConsoleMessageWebRTCMessageSource; case MessageSource::ITPDebug: return WebConsoleMessageITPDebugMessageSource; case MessageSource::PrivateClickMeasurement: return WebConsoleMessagePrivateClickMeasurementMessageSource; case MessageSource::PaymentRequest: return WebConsoleMessagePaymentRequestMessageSource; case MessageSource::Other: return WebConsoleMessageOtherMessageSource; } ASSERT_NOT_REACHED(); return @""; } inline static NSString *stringForMessageLevel(MessageLevel level) { switch (level) { case MessageLevel::Debug: return WebConsoleMessageDebugMessageLevel; case MessageLevel::Log: return WebConsoleMessageLogMessageLevel; case MessageLevel::Info: return WebConsoleMessageInfoMessageLevel; case MessageLevel::Warning: return WebConsoleMessageWarningMessageLevel; case MessageLevel::Error: return WebConsoleMessageErrorMessageLevel; } ASSERT_NOT_REACHED(); return @""; } void WebChromeClient::addMessageToConsole(MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, unsigned columnNumber, const String& sourceURL) { #if !PLATFORM(IOS_FAMILY) id delegate = [m_webView UIDelegate]; #else if (![m_webView _allowsMessaging]) return; id delegate = [m_webView _UIKitDelegate]; // No delegate means nothing to send this data to so bail. if (!delegate) return; #endif BOOL respondsToNewSelector = NO; SEL selector = @selector(webView:addMessageToConsole:withSource:); if ([delegate respondsToSelector:selector]) respondsToNewSelector = YES; else { // The old selector only takes JSMessageSource messages. if (source != MessageSource::JS) return; selector = @selector(webView:addMessageToConsole:); if (![delegate respondsToSelector:selector]) return; } NSString *messageSource = stringForMessageSource(source); auto dictionary = @{ @"message": (NSString *)message, @"lineNumber": @(lineNumber), @"columnNumber": @(columnNumber), @"sourceURL": (NSString *)sourceURL, @"MessageSource": messageSource, @"MessageLevel": stringForMessageLevel(level), }; #if PLATFORM(IOS_FAMILY) [[[m_webView _UIKitDelegateForwarder] asyncForwarder] webView:m_webView addMessageToConsole:dictionary withSource:messageSource]; UNUSED_VARIABLE(respondsToNewSelector); #else if (respondsToNewSelector) CallUIDelegate(m_webView, selector, dictionary, messageSource); else CallUIDelegate(m_webView, selector, dictionary); #endif } bool WebChromeClient::canRunBeforeUnloadConfirmPanel() { return [[m_webView UIDelegate] respondsToSelector:@selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:)]; } bool WebChromeClient::runBeforeUnloadConfirmPanel(const String& message, Frame& frame) { return CallUIDelegateReturningBoolean(true, m_webView, @selector(webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:), message, kit(&frame)); } void WebChromeClient::closeWindow() { // We need to remove the parent WebView from WebViewSets here, before it actually // closes, to make sure that JavaScript code that executes before it closes // can't find it. Otherwise, window.open will select a closed WebView instead of // opening a new one . // We also need to stop the load to prevent further parsing or JavaScript execution // after the window has torn down . // FIXME: This code assumes that the UI delegate will respond to a webViewClose // message by actually closing the WebView. Safari guarantees this behavior, but other apps might not. // This approach is an inherent limitation of not making a close execute immediately // after a call to window.close. [m_webView setGroupName:nil]; [m_webView stopLoading:nil]; [m_webView _closeWindow]; } void WebChromeClient::runJavaScriptAlert(Frame& frame, const String& message) { id delegate = [m_webView UIDelegate]; SEL selector = @selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:); if ([delegate respondsToSelector:selector]) { CallUIDelegate(m_webView, selector, message, kit(&frame)); return; } // Call the old version of the delegate method if it is implemented. selector = @selector(webView:runJavaScriptAlertPanelWithMessage:); if ([delegate respondsToSelector:selector]) { CallUIDelegate(m_webView, selector, message); return; } } bool WebChromeClient::runJavaScriptConfirm(Frame& frame, const String& message) { id delegate = [m_webView UIDelegate]; SEL selector = @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:); if ([delegate respondsToSelector:selector]) return CallUIDelegateReturningBoolean(NO, m_webView, selector, message, kit(&frame)); // Call the old version of the delegate method if it is implemented. selector = @selector(webView:runJavaScriptConfirmPanelWithMessage:); if ([delegate respondsToSelector:selector]) return CallUIDelegateReturningBoolean(NO, m_webView, selector, message); return NO; } bool WebChromeClient::runJavaScriptPrompt(Frame& frame, const String& prompt, const String& defaultText, String& result) { id delegate = [m_webView UIDelegate]; SEL selector = @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:); NSString *defaultString = defaultText; if ([delegate respondsToSelector:selector]) { result = (NSString *)CallUIDelegate(m_webView, selector, prompt, defaultString, kit(&frame)); return !result.isNull(); } // Call the old version of the delegate method if it is implemented. selector = @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:); if ([delegate respondsToSelector:selector]) { result = (NSString *)CallUIDelegate(m_webView, selector, prompt, defaultString); return !result.isNull(); } result = [[WebDefaultUIDelegate sharedUIDelegate] webView:m_webView runJavaScriptTextInputPanelWithPrompt:prompt defaultText:defaultString initiatedByFrame:kit(&frame)]; return !result.isNull(); } void WebChromeClient::setStatusbarText(const String& status) { // We want the temporaries allocated here to be released even before returning to the // event loop; see . @autoreleasepool { CallUIDelegate(m_webView, @selector(webView:setStatusText:), (NSString *)status); } } bool WebChromeClient::supportsImmediateInvalidation() { return true; } void WebChromeClient::invalidateRootView(const IntRect&) { } void WebChromeClient::invalidateContentsAndRootView(const IntRect& rect) { } void WebChromeClient::invalidateContentsForSlowScroll(const IntRect& rect) { invalidateContentsAndRootView(rect); } void WebChromeClient::scroll(const IntSize&, const IntRect&, const IntRect&) { } IntPoint WebChromeClient::screenToRootView(const IntPoint& p) const { // FIXME: Implement this. return p; } IntRect WebChromeClient::rootViewToScreen(const IntRect& r) const { // FIXME: Implement this. return r; } IntPoint WebChromeClient::accessibilityScreenToRootView(const IntPoint& p) const { return screenToRootView(p); } IntRect WebChromeClient::rootViewToAccessibilityScreen(const IntRect& r) const { return rootViewToScreen(r); } void WebChromeClient::didFinishLoadingImageForElement(HTMLImageElement&) { } PlatformPageClient WebChromeClient::platformPageClient() const { return 0; } void WebChromeClient::contentsSizeChanged(Frame&, const IntSize&) const { } void WebChromeClient::scrollContainingScrollViewsToRevealRect(const IntRect& r) const { // FIXME: This scrolling behavior should be under the control of the embedding client, // perhaps in a delegate method, rather than something WebKit does unconditionally. NSView *coordinateView = [[[m_webView mainFrame] frameView] documentView]; NSRect rect = r; for (NSView *view = m_webView; view; view = [view superview]) { if ([view isKindOfClass:[NSClipView class]]) { NSClipView *clipView = (NSClipView *)view; NSView *documentView = [clipView documentView]; [documentView scrollRectToVisible:[documentView convertRect:rect fromView:coordinateView]]; } } } // End host window methods. bool WebChromeClient::shouldUnavailablePluginMessageBeButton(RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) const { if (pluginUnavailabilityReason == RenderEmbeddedObject::PluginMissing) return [[m_webView UIDelegate] respondsToSelector:@selector(webView:didPressMissingPluginButton:)]; return false; } void WebChromeClient::unavailablePluginButtonClicked(Element& element, RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) const { ASSERT(element.hasTagName(objectTag) || element.hasTagName(embedTag) || element.hasTagName(appletTag)); ASSERT(pluginUnavailabilityReason == RenderEmbeddedObject::PluginMissing); CallUIDelegate(m_webView, @selector(webView:didPressMissingPluginButton:), kit(&element)); } void WebChromeClient::mouseDidMoveOverElement(const HitTestResult& result, unsigned modifierFlags, const String& toolTip, TextDirection) { auto element = adoptNS([[WebElementDictionary alloc] initWithHitTestResult:result]); [m_webView _mouseDidMoveOverElement:element.get() modifierFlags:modifierFlags]; setToolTip(toolTip); } void WebChromeClient::setToolTip(const String& toolTip) { NSView *documentView = [[[m_webView _selectedOrMainFrame] frameView] documentView]; if ([documentView isKindOfClass:[WebHTMLView class]]) [(WebHTMLView *)documentView _setToolTip:toolTip]; } void WebChromeClient::print(Frame& frame, const StringWithDirection&) { WebFrame *webFrame = kit(&frame); if ([[m_webView UIDelegate] respondsToSelector:@selector(webView:printFrame:)]) CallUIDelegate(m_webView, @selector(webView:printFrame:), webFrame); else CallUIDelegate(m_webView, @selector(webView:printFrameView:), [webFrame frameView]); } void WebChromeClient::exceededDatabaseQuota(Frame& frame, const String& databaseName, DatabaseDetails) { BEGIN_BLOCK_OBJC_EXCEPTIONS auto webOrigin = adoptNS([[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:&frame.document()->securityOrigin()]); CallUIDelegate(m_webView, @selector(webView:frame:exceededDatabaseQuotaForSecurityOrigin:database:), kit(&frame), webOrigin.get(), (NSString *)databaseName); END_BLOCK_OBJC_EXCEPTIONS } void WebChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded) { // FIXME: Free some space. } void WebChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin& origin, int64_t totalSpaceNeeded) { BEGIN_BLOCK_OBJC_EXCEPTIONS auto webOrigin = adoptNS([[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:&origin]); CallUIDelegate(m_webView, @selector(webView:exceededApplicationCacheOriginQuotaForSecurityOrigin:totalSpaceNeeded:), webOrigin.get(), static_cast(totalSpaceNeeded)); END_BLOCK_OBJC_EXCEPTIONS } #if ENABLE(INPUT_TYPE_COLOR) std::unique_ptr WebChromeClient::createColorChooser(ColorChooserClient& client, const Color& initialColor) { // FIXME: Implement for WK1 (Bug 119094). ASSERT_NOT_REACHED(); return nullptr; } #endif #if ENABLE(DATALIST_ELEMENT) std::unique_ptr WebChromeClient::createDataListSuggestionPicker(DataListSuggestionsClient& client) { ASSERT_NOT_REACHED(); return nullptr; } #endif #if ENABLE(DATE_AND_TIME_INPUT_TYPES) std::unique_ptr WebChromeClient::createDateTimeChooser(DateTimeChooserClient&) { ASSERT_NOT_REACHED(); return nullptr; } #endif #if ENABLE(APP_HIGHLIGHTS) void WebChromeClient::storeAppHighlight(WebCore::AppHighlight&&) const { } #endif void WebChromeClient::setTextIndicator(const WebCore::TextIndicatorData& indicatorData) const { } #if ENABLE(POINTER_LOCK) bool WebChromeClient::requestPointerLock() { #if PLATFORM(MAC) if (![m_webView page]) return false; CGDisplayHideCursor(CGMainDisplayID()); CGAssociateMouseAndMouseCursorPosition(false); [m_webView page]->pointerLockController().didAcquirePointerLock(); return true; #else return false; #endif } void WebChromeClient::requestPointerUnlock() { #if PLATFORM(MAC) CGAssociateMouseAndMouseCursorPosition(true); CGDisplayShowCursor(CGMainDisplayID()); if ([m_webView page]) [m_webView page]->pointerLockController().didLosePointerLock(); #endif } #endif void WebChromeClient::runOpenPanel(Frame&, FileChooser& chooser) { BEGIN_BLOCK_OBJC_EXCEPTIONS BOOL allowMultipleFiles = chooser.settings().allowsMultipleFiles; auto listener = adoptNS([[WebOpenPanelResultListener alloc] initWithChooser:chooser]); id delegate = [m_webView UIDelegate]; if ([delegate respondsToSelector:@selector(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:)]) CallUIDelegate(m_webView, @selector(webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:), listener.get(), allowMultipleFiles); else if ([delegate respondsToSelector:@selector(webView:runOpenPanelForFileButtonWithResultListener:)]) CallUIDelegate(m_webView, @selector(webView:runOpenPanelForFileButtonWithResultListener:), listener.get()); else [listener cancel]; END_BLOCK_OBJC_EXCEPTIONS } void WebChromeClient::showShareSheet(ShareDataWithParsedURL&, CompletionHandler&&) { } void WebChromeClient::loadIconForFiles(const Vector& filenames, FileIconLoader& iconLoader) { iconLoader.iconLoaded(createIconForFiles(filenames)); } RefPtr WebChromeClient::createIconForFiles(const Vector& filenames) { return Icon::createIconForFiles(filenames); } #if !PLATFORM(IOS_FAMILY) void WebChromeClient::setCursor(const WebCore::Cursor& cursor) { // FIXME: Would be nice to share this code with WebKit2's PageClientImpl. if ([NSApp _cursorRectCursor]) return; if (!m_webView) return; NSWindow *window = [m_webView window]; if (!window) return; if ([window windowNumber] != [NSWindow windowNumberAtPoint:[NSEvent mouseLocation] belowWindowWithWindowNumber:0]) return; NSCursor *platformCursor = cursor.platformCursor(); if ([NSCursor currentCursor] == platformCursor) return; [platformCursor set]; } void WebChromeClient::setCursorHiddenUntilMouseMoves(bool hiddenUntilMouseMoves) { [NSCursor setHiddenUntilMouseMoves:hiddenUntilMouseMoves]; } #endif KeyboardUIMode WebChromeClient::keyboardUIMode() { BEGIN_BLOCK_OBJC_EXCEPTIONS return [m_webView _keyboardUIMode]; END_BLOCK_OBJC_EXCEPTIONS return KeyboardAccessDefault; } NSResponder *WebChromeClient::firstResponder() { BEGIN_BLOCK_OBJC_EXCEPTIONS return [[m_webView _UIDelegateForwarder] webViewFirstResponder:m_webView]; END_BLOCK_OBJC_EXCEPTIONS return nil; } void WebChromeClient::makeFirstResponder(NSResponder *responder) { BEGIN_BLOCK_OBJC_EXCEPTIONS [m_webView _pushPerformingProgrammaticFocus]; [[m_webView _UIDelegateForwarder] webView:m_webView makeFirstResponder:responder]; [m_webView _popPerformingProgrammaticFocus]; END_BLOCK_OBJC_EXCEPTIONS } void WebChromeClient::enableSuddenTermination() { #if !PLATFORM(IOS_FAMILY) [[NSProcessInfo processInfo] enableSuddenTermination]; #endif } void WebChromeClient::disableSuddenTermination() { #if !PLATFORM(IOS_FAMILY) [[NSProcessInfo processInfo] disableSuddenTermination]; #endif } #if !PLATFORM(IOS_FAMILY) void WebChromeClient::elementDidFocus(WebCore::Element& element, const WebCore::FocusOptions&) { CallUIDelegate(m_webView, @selector(webView:formDidFocusNode:), kit(&element)); } void WebChromeClient::elementDidBlur(WebCore::Element& element) { CallUIDelegate(m_webView, @selector(webView:formDidBlurNode:), kit(&element)); } #endif bool WebChromeClient::selectItemWritingDirectionIsNatural() { return false; } bool WebChromeClient::selectItemAlignmentFollowsMenuWritingDirection() { return true; } RefPtr WebChromeClient::createPopupMenu(WebCore::PopupMenuClient& client) const { #if !PLATFORM(IOS_FAMILY) return adoptRef(*new PopupMenuMac(&client)); #else return nullptr; #endif } RefPtr WebChromeClient::createSearchPopupMenu(WebCore::PopupMenuClient& client) const { #if !PLATFORM(IOS_FAMILY) return adoptRef(*new SearchPopupMenuMac(&client)); #else return nullptr; #endif } bool WebChromeClient::shouldPaintEntireContents() const { #if PLATFORM(IOS_FAMILY) return false; #else NSView *documentView = [[[m_webView mainFrame] frameView] documentView]; return [documentView layer]; #endif } void WebChromeClient::attachRootGraphicsLayer(Frame& frame, GraphicsLayer* graphicsLayer) { #if !PLATFORM(MAC) UNUSED_PARAM(frame); UNUSED_PARAM(graphicsLayer); #else BEGIN_BLOCK_OBJC_EXCEPTIONS NSView *documentView = [[kit(&frame) frameView] documentView]; if (![documentView isKindOfClass:[WebHTMLView class]]) { // We should never be attaching when we don't have a WebHTMLView. ASSERT(!graphicsLayer); return; } WebHTMLView *webHTMLView = (WebHTMLView *)documentView; if (graphicsLayer) [webHTMLView attachRootLayer:graphicsLayer->platformLayer()]; else [webHTMLView detachRootLayer]; END_BLOCK_OBJC_EXCEPTIONS #endif } void WebChromeClient::attachViewOverlayGraphicsLayer(GraphicsLayer*) { // FIXME: If we want view-relative page overlays in Legacy WebKit, this would be the place to hook them up. } void WebChromeClient::setNeedsOneShotDrawingSynchronization() { BEGIN_BLOCK_OBJC_EXCEPTIONS [m_webView _setNeedsOneShotDrawingSynchronization:YES]; END_BLOCK_OBJC_EXCEPTIONS } void WebChromeClient::triggerRenderingUpdate() { BEGIN_BLOCK_OBJC_EXCEPTIONS [m_webView _scheduleUpdateRendering]; END_BLOCK_OBJC_EXCEPTIONS } #if ENABLE(VIDEO) bool WebChromeClient::canEnterVideoFullscreen() const { #if !PLATFORM(IOS_FAMILY) || HAVE(AVKIT) return true; #else return false; #endif } bool WebChromeClient::supportsVideoFullscreen(HTMLMediaElementEnums::VideoFullscreenMode) { #if !PLATFORM(IOS_FAMILY) || HAVE(AVKIT) return true; #else return false; #endif } #if ENABLE(VIDEO_PRESENTATION_MODE) void WebChromeClient::setMockVideoPresentationModeEnabled(bool enabled) { m_mockVideoPresentationModeEnabled = enabled; } void WebChromeClient::enterVideoFullscreenForVideoElement(HTMLVideoElement& videoElement, HTMLMediaElementEnums::VideoFullscreenMode mode, bool standby) { ASSERT_UNUSED(standby, !standby); ASSERT(mode != HTMLMediaElementEnums::VideoFullscreenModeNone); BEGIN_BLOCK_OBJC_EXCEPTIONS if (m_mockVideoPresentationModeEnabled) videoElement.didBecomeFullscreenElement(); else [m_webView _enterVideoFullscreenForVideoElement:&videoElement mode:mode]; END_BLOCK_OBJC_EXCEPTIONS } void WebChromeClient::exitVideoFullscreenForVideoElement(WebCore::HTMLVideoElement& videoElement, CompletionHandler&& completionHandler) { BEGIN_BLOCK_OBJC_EXCEPTIONS if (m_mockVideoPresentationModeEnabled) videoElement.didStopBeingFullscreenElement(); else [m_webView _exitVideoFullscreen]; END_BLOCK_OBJC_EXCEPTIONS completionHandler(true); } void WebChromeClient::exitVideoFullscreenToModeWithoutAnimation(HTMLVideoElement& videoElement, HTMLMediaElementEnums::VideoFullscreenMode targetMode) { BEGIN_BLOCK_OBJC_EXCEPTIONS if (m_mockVideoPresentationModeEnabled) videoElement.didStopBeingFullscreenElement(); else [m_webView _exitVideoFullscreen]; END_BLOCK_OBJC_EXCEPTIONS } #endif // ENABLE(VIDEO_PRESENTATION_MODE) #endif // ENABLE(VIDEO) #if ENABLE(VIDEO) && PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE) void WebChromeClient::setUpPlaybackControlsManager(HTMLMediaElement& element) { [m_webView _setUpPlaybackControlsManagerForMediaElement:element]; } void WebChromeClient::clearPlaybackControlsManager() { [m_webView _clearPlaybackControlsManager]; } void WebChromeClient::playbackControlsMediaEngineChanged() { [m_webView _playbackControlsMediaEngineChanged]; } #endif #if ENABLE(FULLSCREEN_API) bool WebChromeClient::supportsFullScreenForElement(const Element& element, bool withKeyboard) { SEL selector = @selector(webView:supportsFullScreenForElement:withKeyboard:); if ([[m_webView UIDelegate] respondsToSelector:selector]) return CallUIDelegateReturningBoolean(false, m_webView, selector, kit(const_cast(&element)), withKeyboard); #if !PLATFORM(IOS_FAMILY) return [m_webView _supportsFullScreenForElement:const_cast(&element) withKeyboard:withKeyboard]; #else return NO; #endif } void WebChromeClient::enterFullScreenForElement(Element& element) { SEL selector = @selector(webView:enterFullScreenForElement:listener:); if ([[m_webView UIDelegate] respondsToSelector:selector]) { auto listener = adoptNS([[WebKitFullScreenListener alloc] initWithElement:&element]); CallUIDelegate(m_webView, selector, kit(&element), listener.get()); } #if !PLATFORM(IOS_FAMILY) else [m_webView _enterFullScreenForElement:&element]; #endif } void WebChromeClient::exitFullScreenForElement(Element* element) { SEL selector = @selector(webView:exitFullScreenForElement:listener:); if ([[m_webView UIDelegate] respondsToSelector:selector]) { auto listener = adoptNS([[WebKitFullScreenListener alloc] initWithElement:element]); CallUIDelegate(m_webView, selector, kit(element), listener.get()); } #if !PLATFORM(IOS_FAMILY) else [m_webView _exitFullScreenForElement:element]; #endif } #endif // ENABLE(FULLSCREEN_API) #if ENABLE(WEB_CRYPTO) bool WebChromeClient::wrapCryptoKey(const Vector& key, Vector& wrappedKey) const { SEL selector = @selector(webCryptoMasterKeyForWebView:); if ([[m_webView UIDelegate] respondsToSelector:selector]) { NSData *keyData = CallUIDelegate(m_webView, selector); Vector masterKey; masterKey.append(static_cast(const_cast([keyData bytes])), [keyData length]); return wrapSerializedCryptoKey(masterKey, key, wrappedKey); } auto masterKey = defaultWebCryptoMasterKey(); if (!masterKey) return false; return wrapSerializedCryptoKey(WTFMove(*masterKey), key, wrappedKey); } bool WebChromeClient::unwrapCryptoKey(const Vector& wrappedKey, Vector& key) const { SEL selector = @selector(webCryptoMasterKeyForWebView:); if ([[m_webView UIDelegate] respondsToSelector:selector]) { Vector masterKey; NSData *keyData = CallUIDelegate(m_webView, selector); masterKey.append(static_cast(const_cast([keyData bytes])), [keyData length]); return unwrapSerializedCryptoKey(masterKey, wrappedKey, key); } auto masterKey = defaultWebCryptoMasterKey(); if (!masterKey) return false; return unwrapSerializedCryptoKey(WTFMove(*masterKey), wrappedKey, key); } #endif #if ENABLE(SERVICE_CONTROLS) void WebChromeClient::handleSelectionServiceClick(WebCore::FrameSelection& selection, const Vector& telephoneNumbers, const WebCore::IntPoint& point) { [m_webView _selectionServiceController].handleSelectionServiceClick(selection, telephoneNumbers, point); } bool WebChromeClient::hasRelevantSelectionServices(bool isTextOnly) const { return [m_webView _selectionServiceController].hasRelevantSelectionServices(isTextOnly); } #endif #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS_FAMILY) void WebChromeClient::addPlaybackTargetPickerClient(PlaybackTargetClientContextIdentifier contextId) { [m_webView _addPlaybackTargetPickerClient:contextId]; } void WebChromeClient::removePlaybackTargetPickerClient(PlaybackTargetClientContextIdentifier contextId) { [m_webView _removePlaybackTargetPickerClient:contextId]; } void WebChromeClient::showPlaybackTargetPicker(PlaybackTargetClientContextIdentifier contextId, const WebCore::IntPoint& location, bool hasVideo) { [m_webView _showPlaybackTargetPicker:contextId location:location hasVideo:hasVideo]; } void WebChromeClient::playbackTargetPickerClientStateDidChange(PlaybackTargetClientContextIdentifier contextId, MediaProducerMediaStateFlags state) { [m_webView _playbackTargetPickerClientStateDidChange:contextId state:state]; } void WebChromeClient::setMockMediaPlaybackTargetPickerEnabled(bool enabled) { [m_webView _setMockMediaPlaybackTargetPickerEnabled:enabled]; } void WebChromeClient::setMockMediaPlaybackTargetPickerState(const String& name, MediaPlaybackTargetContext::MockState state) { [m_webView _setMockMediaPlaybackTargetPickerName:name state:state]; } void WebChromeClient::mockMediaPlaybackTargetPickerDismissPopup() { [m_webView _mockMediaPlaybackTargetPickerDismissPopup]; } #endif String WebChromeClient::signedPublicKeyAndChallengeString(unsigned keySizeIndex, const String& challengeString, const URL& url) const { SEL selector = @selector(signedPublicKeyAndChallengeStringForWebView:); if ([[m_webView UIDelegate] respondsToSelector:selector]) return CallUIDelegate(m_webView, selector); return WebCore::signedPublicKeyAndChallengeString(keySizeIndex, challengeString, url); } #if PLATFORM(MAC) void WebChromeClient::changeUniversalAccessZoomFocus(const WebCore::IntRect& viewRect, const WebCore::IntRect& selectionRect) { WebCore::changeUniversalAccessZoomFocus(viewRect, selectionRect); } #endif RefPtr WebChromeClient::createGPUForWebGPU() const { #if HAVE(WEBGPU_IMPLEMENTATION) return PAL::WebGPU::create([](PAL::WebGPU::WorkItem&& workItem) { callOnMainRunLoop(WTFMove(workItem)); }); #else return nullptr; #endif } void WebChromeClient::requestCookieConsent(CompletionHandler&& completion) { completion(CookieConsentDecisionResult::NotSupported); } void WebChromeClient::classifyModalContainerControls(Vector&&, CompletionHandler&&)>&& completion) { completion({ }); } void WebChromeClient::decidePolicyForModalContainer(OptionSet, CompletionHandler&& completion) { completion(ModalContainerDecision::Show); }