blob: 83a00d7c3baeee69028e446bac0a211f2d0e6bf7 [file] [log] [blame]
/*
* Copyright (C) 2012-2019 Apple Inc. All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "config.h"
#import "WebPageProxy.h"
#if PLATFORM(IOS_FAMILY)
#import "APINavigationAction.h"
#import "APIPageConfiguration.h"
#import "APIUIClient.h"
#import "APIWebsitePolicies.h"
#import "Connection.h"
#import "DataReference.h"
#import "DocumentEditingContext.h"
#import "EditingRange.h"
#import "GlobalFindInPageState.h"
#import "InteractionInformationAtPosition.h"
#import "Logging.h"
#import "NativeWebKeyboardEvent.h"
#import "NavigationState.h"
#import "PageClient.h"
#import "PaymentAuthorizationViewController.h"
#import "PrintInfo.h"
#import "RemoteLayerTreeDrawingAreaProxy.h"
#import "RemoteLayerTreeDrawingAreaProxyMessages.h"
#import "RemoteLayerTreeTransaction.h"
#import "RemoteScrollingCoordinatorProxy.h"
#import "ShareableResource.h"
#import "TapHandlingResult.h"
#import "UIKitSPI.h"
#import "UserData.h"
#import "UserInterfaceIdiom.h"
#import "VideoFullscreenManagerProxy.h"
#import "ViewUpdateDispatcherMessages.h"
#import "WKBrowsingContextControllerInternal.h"
#import "WebAutocorrectionContext.h"
#import "WebAutocorrectionData.h"
#import "WebPageMessages.h"
#import "WebProcessMessages.h"
#import "WebProcessPool.h"
#import "WebProcessProxy.h"
#import <WebCore/AGXCompilerService.h>
#import <WebCore/FrameView.h>
#import <WebCore/NotImplemented.h>
#import <WebCore/PlatformScreen.h>
#import <WebCore/RuntimeApplicationChecks.h>
#import <WebCore/SharedBuffer.h>
#import <WebCore/UserAgent.h>
#import <WebCore/ValidationBubble.h>
#import <pal/spi/ios/MobileGestaltSPI.h>
#import <wtf/cocoa/RuntimeApplicationChecksCocoa.h>
#import <wtf/text/TextStream.h>
#if USE(QUICK_LOOK)
#import "APILoaderClient.h"
#import "APINavigationClient.h"
#import <wtf/text/WTFString.h>
#endif
#define MESSAGE_CHECK(assertion) MESSAGE_CHECK_BASE(assertion, process().connection())
#define WEBPAGEPROXY_RELEASE_LOG(channel, fmt, ...) RELEASE_LOG(channel, "%p - [pageProxyID=%llu, webPageID=%llu, PID=%i] WebPageProxy::" fmt, this, m_identifier.toUInt64(), m_webPageID.toUInt64(), m_process->processIdentifier(), ##__VA_ARGS__)
namespace WebKit {
using namespace WebCore;
void WebPageProxy::platformInitialize()
{
}
String WebPageProxy::userAgentForURL(const URL&)
{
return userAgent();
}
String WebPageProxy::standardUserAgent(const String& applicationNameForUserAgent)
{
return standardUserAgentWithApplicationName(applicationNameForUserAgent);
}
void WebPageProxy::getIsSpeaking(CompletionHandler<void(bool)>&& completionHandler)
{
notImplemented();
completionHandler(false);
}
void WebPageProxy::speak(const String&)
{
notImplemented();
}
void WebPageProxy::stopSpeaking()
{
notImplemented();
}
void WebPageProxy::searchWithSpotlight(const String&)
{
notImplemented();
}
void WebPageProxy::searchTheWeb(const String&)
{
notImplemented();
}
void WebPageProxy::windowAndViewFramesChanged(const FloatRect&, const FloatPoint&)
{
notImplemented();
}
String WebPageProxy::stringSelectionForPasteboard()
{
notImplemented();
return String();
}
RefPtr<WebCore::SharedBuffer> WebPageProxy::dataSelectionForPasteboard(const String&)
{
notImplemented();
return nullptr;
}
bool WebPageProxy::readSelectionFromPasteboard(const String&)
{
notImplemented();
return false;
}
void WebPageProxy::requestFocusedElementInformation(CompletionHandler<void(const std::optional<FocusedElementInformation>&)>&& callback)
{
if (!hasRunningProcess())
return callback({ });
sendWithAsyncReply(Messages::WebPage::RequestFocusedElementInformation(), WTFMove(callback));
}
void WebPageProxy::updateVisibleContentRects(const VisibleContentRectUpdateInfo& visibleContentRectUpdate, bool sendEvenIfUnchanged)
{
if (visibleContentRectUpdate == m_lastVisibleContentRectUpdate && !sendEvenIfUnchanged)
return;
m_lastVisibleContentRectUpdate = visibleContentRectUpdate;
if (!hasRunningProcess())
return;
m_process->send(Messages::ViewUpdateDispatcher::VisibleContentRectUpdate(m_webPageID, visibleContentRectUpdate), 0);
}
void WebPageProxy::resendLastVisibleContentRects()
{
m_process->send(Messages::ViewUpdateDispatcher::VisibleContentRectUpdate(m_webPageID, m_lastVisibleContentRectUpdate), 0);
}
void WebPageProxy::updateStringForFind(const String& string)
{
if (!hasRunningProcess())
return;
WebKit::updateStringForFind(string);
}
static inline float adjustedUnexposedEdge(float documentEdge, float exposedRectEdge, float factor)
{
if (exposedRectEdge < documentEdge)
return documentEdge - factor * (documentEdge - exposedRectEdge);
return exposedRectEdge;
}
static inline float adjustedUnexposedMaxEdge(float documentEdge, float exposedRectEdge, float factor)
{
if (exposedRectEdge > documentEdge)
return documentEdge + factor * (exposedRectEdge - documentEdge);
return exposedRectEdge;
}
WebCore::FloatRect WebPageProxy::computeLayoutViewportRect(const FloatRect& unobscuredContentRect, const FloatRect& unobscuredContentRectRespectingInputViewBounds, const FloatRect& currentLayoutViewportRect, double displayedContentScale, FrameView::LayoutViewportConstraint constraint) const
{
FloatRect constrainedUnobscuredRect = unobscuredContentRect;
FloatRect documentRect = pageClient().documentRect();
if (constraint == FrameView::LayoutViewportConstraint::ConstrainedToDocumentRect)
constrainedUnobscuredRect.intersect(documentRect);
double minimumScale = pageClient().minimumZoomScale();
bool isBelowMinimumScale = displayedContentScale < minimumScale;
if (isBelowMinimumScale) {
const CGFloat slope = 12;
CGFloat factor = std::max<CGFloat>(1 - slope * (minimumScale - displayedContentScale), 0);
constrainedUnobscuredRect.setX(adjustedUnexposedEdge(documentRect.x(), constrainedUnobscuredRect.x(), factor));
constrainedUnobscuredRect.setY(adjustedUnexposedEdge(documentRect.y(), constrainedUnobscuredRect.y(), factor));
constrainedUnobscuredRect.setWidth(adjustedUnexposedMaxEdge(documentRect.maxX(), constrainedUnobscuredRect.maxX(), factor) - constrainedUnobscuredRect.x());
constrainedUnobscuredRect.setHeight(adjustedUnexposedMaxEdge(documentRect.maxY(), constrainedUnobscuredRect.maxY(), factor) - constrainedUnobscuredRect.y());
}
FloatSize constrainedSize = isBelowMinimumScale ? constrainedUnobscuredRect.size() : unobscuredContentRect.size();
FloatRect unobscuredContentRectForViewport = isBelowMinimumScale ? constrainedUnobscuredRect : unobscuredContentRectRespectingInputViewBounds;
auto layoutViewportSize = FrameView::expandedLayoutViewportSize(m_baseLayoutViewportSize, LayoutSize(documentRect.size()), m_preferences->layoutViewportHeightExpansionFactor());
FloatRect layoutViewportRect = FrameView::computeUpdatedLayoutViewportRect(LayoutRect(currentLayoutViewportRect), LayoutRect(documentRect), LayoutSize(constrainedSize), LayoutRect(unobscuredContentRectForViewport), layoutViewportSize, m_minStableLayoutViewportOrigin, m_maxStableLayoutViewportOrigin, constraint);
if (layoutViewportRect != currentLayoutViewportRect)
LOG_WITH_STREAM(VisibleRects, stream << "WebPageProxy::computeLayoutViewportRect: new layout viewport " << layoutViewportRect);
return layoutViewportRect;
}
FloatRect WebPageProxy::unconstrainedLayoutViewportRect() const
{
return computeLayoutViewportRect(unobscuredContentRect(), unobscuredContentRectRespectingInputViewBounds(), layoutViewportRect(), displayedContentScale(), FrameView::LayoutViewportConstraint::Unconstrained);
}
void WebPageProxy::adjustLayersForLayoutViewport(const FloatRect& layoutViewport)
{
if (!m_scrollingCoordinatorProxy)
return;
m_scrollingCoordinatorProxy->viewportChangedViaDelegatedScrolling(unobscuredContentRect().location(), layoutViewport, displayedContentScale());
}
void WebPageProxy::scrollingNodeScrollViewWillStartPanGesture()
{
pageClient().scrollingNodeScrollViewWillStartPanGesture();
}
void WebPageProxy::scrollingNodeScrollViewDidScroll()
{
pageClient().scrollingNodeScrollViewDidScroll();
}
void WebPageProxy::scrollingNodeScrollWillStartScroll()
{
pageClient().scrollingNodeScrollWillStartScroll();
}
void WebPageProxy::scrollingNodeScrollDidEndScroll()
{
pageClient().scrollingNodeScrollDidEndScroll();
}
void WebPageProxy::dynamicViewportSizeUpdate(const FloatSize& viewLayoutSize, const WebCore::FloatSize& minimumUnobscuredSize, const WebCore::FloatSize& maximumUnobscuredSize, const FloatRect& targetExposedContentRect, const FloatRect& targetUnobscuredRect, const FloatRect& targetUnobscuredRectInScrollViewCoordinates, const WebCore::FloatBoxExtent& unobscuredSafeAreaInsets, double targetScale, int32_t deviceOrientation, double minimumEffectiveDeviceWidth, DynamicViewportSizeUpdateID dynamicViewportSizeUpdateID)
{
if (!hasRunningProcess())
return;
hideValidationMessage();
m_viewportConfigurationViewLayoutSize = viewLayoutSize;
m_process->send(Messages::WebPage::DynamicViewportSizeUpdate(viewLayoutSize,
minimumUnobscuredSize, maximumUnobscuredSize, targetExposedContentRect, targetUnobscuredRect,
targetUnobscuredRectInScrollViewCoordinates, unobscuredSafeAreaInsets,
targetScale, deviceOrientation, minimumEffectiveDeviceWidth, dynamicViewportSizeUpdateID), m_webPageID);
setDeviceOrientation(deviceOrientation);
}
void WebPageProxy::setViewportConfigurationViewLayoutSize(const WebCore::FloatSize& size, double scaleFactor, double minimumEffectiveDeviceWidth)
{
m_viewportConfigurationViewLayoutSize = size;
m_viewportConfigurationLayoutSizeScaleFactor = scaleFactor;
m_viewportConfigurationMinimumEffectiveDeviceWidth = minimumEffectiveDeviceWidth;
if (hasRunningProcess())
m_process->send(Messages::WebPage::SetViewportConfigurationViewLayoutSize(size, scaleFactor, minimumEffectiveDeviceWidth), m_webPageID);
}
void WebPageProxy::setForceAlwaysUserScalable(bool userScalable)
{
if (m_forceAlwaysUserScalable == userScalable)
return;
m_forceAlwaysUserScalable = userScalable;
if (hasRunningProcess())
m_process->send(Messages::WebPage::SetForceAlwaysUserScalable(userScalable), m_webPageID);
}
void WebPageProxy::setMinimumUnobscuredSize(const WebCore::FloatSize& size)
{
m_minimumUnobscuredSize = size;
if (hasRunningProcess())
m_process->send(Messages::WebPage::SetMinimumUnobscuredSize(size), m_webPageID);
}
void WebPageProxy::setMaximumUnobscuredSize(const WebCore::FloatSize& size)
{
m_maximumUnobscuredSize = size;
if (hasRunningProcess())
m_process->send(Messages::WebPage::SetMaximumUnobscuredSize(size), m_webPageID);
}
void WebPageProxy::setDeviceOrientation(int32_t deviceOrientation)
{
if (deviceOrientation != m_deviceOrientation) {
m_deviceOrientation = deviceOrientation;
if (hasRunningProcess()) {
m_process->send(Messages::WebPage::SetDeviceOrientation(deviceOrientation), m_webPageID);
setOrientationForMediaCapture(deviceOrientation);
}
}
}
void WebPageProxy::setOverrideViewportArguments(const std::optional<ViewportArguments>& viewportArguments)
{
if (viewportArguments == m_overrideViewportArguments)
return;
m_overrideViewportArguments = viewportArguments;
if (hasRunningProcess())
m_process->send(Messages::WebPage::SetOverrideViewportArguments(viewportArguments), m_webPageID);
}
static bool exceedsRenderTreeSizeSizeThreshold(uint64_t thresholdSize, uint64_t committedSize)
{
const double thesholdSizeFraction = 0.5; // Empirically-derived.
return committedSize > thresholdSize * thesholdSizeFraction;
}
void WebPageProxy::didCommitLayerTree(const WebKit::RemoteLayerTreeTransaction& layerTreeTransaction)
{
themeColorChanged(layerTreeTransaction.themeColor());
pageExtendedBackgroundColorDidChange(layerTreeTransaction.pageExtendedBackgroundColor());
sampledPageTopColorChanged(layerTreeTransaction.sampledPageTopColor());
if (!m_hasUpdatedRenderingAfterDidCommitLoad) {
if (layerTreeTransaction.transactionID() >= m_firstLayerTreeTransactionIdAfterDidCommitLoad) {
m_hasUpdatedRenderingAfterDidCommitLoad = true;
stopMakingViewBlankDueToLackOfRenderingUpdateIfNecessary();
m_lastVisibleContentRectUpdate = VisibleContentRectUpdateInfo();
}
}
pageClient().didCommitLayerTree(layerTreeTransaction);
// FIXME: Remove this special mechanism and fold it into the transaction's layout milestones.
if (m_observedLayoutMilestones.contains(WebCore::ReachedSessionRestorationRenderTreeSizeThreshold) && !m_hitRenderTreeSizeThreshold
&& exceedsRenderTreeSizeSizeThreshold(m_sessionRestorationRenderTreeSize, layerTreeTransaction.renderTreeSize())) {
m_hitRenderTreeSizeThreshold = true;
didReachLayoutMilestone(WebCore::ReachedSessionRestorationRenderTreeSizeThreshold);
}
}
bool WebPageProxy::updateLayoutViewportParameters(const WebKit::RemoteLayerTreeTransaction& layerTreeTransaction)
{
if (m_baseLayoutViewportSize == layerTreeTransaction.baseLayoutViewportSize()
&& m_minStableLayoutViewportOrigin == layerTreeTransaction.minStableLayoutViewportOrigin()
&& m_maxStableLayoutViewportOrigin == layerTreeTransaction.maxStableLayoutViewportOrigin())
return false;
m_baseLayoutViewportSize = layerTreeTransaction.baseLayoutViewportSize();
m_minStableLayoutViewportOrigin = layerTreeTransaction.minStableLayoutViewportOrigin();
m_maxStableLayoutViewportOrigin = layerTreeTransaction.maxStableLayoutViewportOrigin();
LOG_WITH_STREAM(VisibleRects, stream << "WebPageProxy::updateLayoutViewportParameters: baseLayoutViewportSize: " << m_baseLayoutViewportSize << " minStableLayoutViewportOrigin: " << m_minStableLayoutViewportOrigin << " maxStableLayoutViewportOrigin: " << m_maxStableLayoutViewportOrigin);
return true;
}
void WebPageProxy::layerTreeCommitComplete()
{
pageClient().layerTreeCommitComplete();
}
void WebPageProxy::selectWithGesture(const WebCore::IntPoint point, GestureType gestureType, GestureRecognizerState gestureState, bool isInteractingWithFocusedElement, CompletionHandler<void(const WebCore::IntPoint&, GestureType, GestureRecognizerState, OptionSet<WebKit::SelectionFlags>)>&& callback)
{
if (!hasRunningProcess())
return callback({ }, GestureType::Loupe, GestureRecognizerState::Possible, { });
sendWithAsyncReply(Messages::WebPage::SelectWithGesture(point, gestureType, gestureState, isInteractingWithFocusedElement), WTFMove(callback));
}
void WebPageProxy::updateSelectionWithTouches(const WebCore::IntPoint point, SelectionTouch touches, bool baseIsStart, CompletionHandler<void(const WebCore::IntPoint&, SelectionTouch, OptionSet<SelectionFlags>)>&& callback)
{
if (!hasRunningProcess())
return callback(WebCore::IntPoint(), SelectionTouch::Started, { });
sendWithAsyncReply(Messages::WebPage::UpdateSelectionWithTouches(point, touches, baseIsStart), WTFMove(callback));
}
void WebPageProxy::replaceDictatedText(const String& oldText, const String& newText)
{
m_process->send(Messages::WebPage::ReplaceDictatedText(oldText, newText), m_webPageID);
}
void WebPageProxy::replaceSelectedText(const String& oldText, const String& newText)
{
m_process->send(Messages::WebPage::ReplaceSelectedText(oldText, newText), m_webPageID);
}
void WebPageProxy::insertTextPlaceholder(const IntSize& size, CompletionHandler<void(const std::optional<ElementContext>&)>&& completionHandler)
{
if (!hasRunningProcess()) {
completionHandler({ });
return;
}
sendWithAsyncReply(Messages::WebPage::InsertTextPlaceholder { size }, WTFMove(completionHandler));
}
void WebPageProxy::removeTextPlaceholder(const ElementContext& placeholder, CompletionHandler<void()>&& completionHandler)
{
if (!hasRunningProcess()) {
completionHandler();
return;
}
sendWithAsyncReply(Messages::WebPage::RemoveTextPlaceholder { placeholder }, WTFMove(completionHandler));
}
void WebPageProxy::requestAutocorrectionData(const String& textForAutocorrection, CompletionHandler<void(WebAutocorrectionData)>&& callback)
{
if (!hasRunningProcess()) {
callback({ });
return;
}
sendWithAsyncReply(Messages::WebPage::RequestAutocorrectionData(textForAutocorrection), WTFMove(callback));
}
void WebPageProxy::applyAutocorrection(const String& correction, const String& originalText, CompletionHandler<void(const String&)>&& callback)
{
sendWithAsyncReply(Messages::WebPage::ApplyAutocorrection(correction, originalText), WTFMove(callback));
}
bool WebPageProxy::applyAutocorrection(const String& correction, const String& originalText)
{
bool autocorrectionApplied = false;
m_process->sendSync(Messages::WebPage::SyncApplyAutocorrection(correction, originalText), Messages::WebPage::SyncApplyAutocorrection::Reply(autocorrectionApplied), m_webPageID);
return autocorrectionApplied;
}
void WebPageProxy::selectTextWithGranularityAtPoint(const WebCore::IntPoint point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement, CompletionHandler<void()>&& callbackFunction)
{
if (!hasRunningProcess()) {
callbackFunction();
return;
}
sendWithAsyncReply(Messages::WebPage::SelectTextWithGranularityAtPoint(point, granularity, isInteractingWithFocusedElement), [callbackFunction = WTFMove(callbackFunction), backgroundActivity = m_process->throttler().backgroundActivity("WebPageProxy::selectTextWithGranularityAtPoint"_s)] () mutable {
callbackFunction();
});
}
void WebPageProxy::selectPositionAtBoundaryWithDirection(const WebCore::IntPoint point, WebCore::TextGranularity granularity, WebCore::SelectionDirection direction, bool isInteractingWithFocusedElement, CompletionHandler<void()>&& callbackFunction)
{
if (!hasRunningProcess()) {
callbackFunction();
return;
}
sendWithAsyncReply(Messages::WebPage::SelectPositionAtBoundaryWithDirection(point, granularity, direction, isInteractingWithFocusedElement), [callbackFunction = WTFMove(callbackFunction), backgroundActivity = m_process->throttler().backgroundActivity("WebPageProxy::selectPositionAtBoundaryWithDirection"_s)] () mutable {
callbackFunction();
});
}
void WebPageProxy::moveSelectionAtBoundaryWithDirection(WebCore::TextGranularity granularity, WebCore::SelectionDirection direction, CompletionHandler<void()>&& callbackFunction)
{
if (!hasRunningProcess()) {
callbackFunction();
return;
}
sendWithAsyncReply(Messages::WebPage::MoveSelectionAtBoundaryWithDirection(granularity, direction), [callbackFunction = WTFMove(callbackFunction), backgroundActivity = m_process->throttler().backgroundActivity("WebPageProxy::moveSelectionAtBoundaryWithDirection"_s)] () mutable {
callbackFunction();
});
}
void WebPageProxy::selectPositionAtPoint(const WebCore::IntPoint point, bool isInteractingWithFocusedElement, CompletionHandler<void()>&& callbackFunction)
{
if (!hasRunningProcess()) {
callbackFunction();
return;
}
sendWithAsyncReply(Messages::WebPage::SelectPositionAtPoint(point, isInteractingWithFocusedElement), [callbackFunction = WTFMove(callbackFunction), backgroundActivity = m_process->throttler().backgroundActivity("WebPageProxy::selectPositionAtPoint"_s)] () mutable {
callbackFunction();
});
}
void WebPageProxy::beginSelectionInDirection(WebCore::SelectionDirection direction, CompletionHandler<void(bool)>&& callback)
{
sendWithAsyncReply(Messages::WebPage::BeginSelectionInDirection(direction), WTFMove(callback));
}
void WebPageProxy::updateSelectionWithExtentPoint(const WebCore::IntPoint point, bool isInteractingWithFocusedElement, RespectSelectionAnchor respectSelectionAnchor, CompletionHandler<void(bool)>&& callback)
{
sendWithAsyncReply(Messages::WebPage::UpdateSelectionWithExtentPoint(point, isInteractingWithFocusedElement, respectSelectionAnchor), WTFMove(callback));
}
void WebPageProxy::updateSelectionWithExtentPointAndBoundary(const WebCore::IntPoint point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement, CompletionHandler<void(bool)>&& callback)
{
sendWithAsyncReply(Messages::WebPage::UpdateSelectionWithExtentPointAndBoundary(point, granularity, isInteractingWithFocusedElement), WTFMove(callback));
}
void WebPageProxy::requestDictationContext(CompletionHandler<void(const String&, const String&, const String&)>&& callbackFunction)
{
if (!hasRunningProcess())
return callbackFunction({ }, { }, { });
sendWithAsyncReply(Messages::WebPage::RequestDictationContext(), WTFMove(callbackFunction));
}
void WebPageProxy::requestAutocorrectionContext()
{
m_process->send(Messages::WebPage::RequestAutocorrectionContext(), m_webPageID);
}
void WebPageProxy::handleAutocorrectionContext(const WebAutocorrectionContext& context)
{
pageClient().handleAutocorrectionContext(context);
}
void WebPageProxy::getSelectionContext(CompletionHandler<void(const String&, const String&, const String&)>&& callbackFunction)
{
if (!hasRunningProcess())
return callbackFunction({ }, { }, { });
sendWithAsyncReply(Messages::WebPage::GetSelectionContext(), WTFMove(callbackFunction));
}
void WebPageProxy::handleTwoFingerTapAtPoint(const WebCore::IntPoint& point, OptionSet<WebEvent::Modifier> modifiers, WebKit::TapIdentifier requestID)
{
send(Messages::WebPage::HandleTwoFingerTapAtPoint(point, modifiers, requestID));
}
void WebPageProxy::selectWithTwoTouches(const WebCore::IntPoint from, const WebCore::IntPoint to, GestureType gestureType, GestureRecognizerState gestureState, CompletionHandler<void(const WebCore::IntPoint&, GestureType, GestureRecognizerState, OptionSet<SelectionFlags>)>&& callback)
{
if (!hasRunningProcess())
return callback({ }, GestureType::Loupe, GestureRecognizerState::Possible, { });
sendWithAsyncReply(Messages::WebPage::SelectWithTwoTouches(from, to, gestureType, gestureState), WTFMove(callback));
}
void WebPageProxy::didReceivePositionInformation(const InteractionInformationAtPosition& info)
{
pageClient().positionInformationDidChange(info);
}
void WebPageProxy::requestPositionInformation(const InteractionInformationRequest& request)
{
m_process->send(Messages::WebPage::RequestPositionInformation(request), m_webPageID);
}
void WebPageProxy::startInteractionWithPositionInformation(const InteractionInformationAtPosition& positionInformation)
{
m_process->send(Messages::WebPage::StartInteractionWithElementContextOrPosition(positionInformation.elementContext, positionInformation.request.point), m_webPageID);
}
void WebPageProxy::stopInteraction()
{
m_process->send(Messages::WebPage::StopInteraction(), m_webPageID);
}
void WebPageProxy::performActionOnElement(uint32_t action)
{
m_process->send(Messages::WebPage::PerformActionOnElement(action), m_webPageID);
}
void WebPageProxy::saveImageToLibrary(const SharedMemory::IPCHandle& imageHandle)
{
MESSAGE_CHECK(!imageHandle.handle.isNull());
auto sharedMemoryBuffer = SharedMemory::map(imageHandle.handle, SharedMemory::Protection::ReadOnly);
if (!sharedMemoryBuffer)
return;
auto buffer = sharedMemoryBuffer->createSharedBuffer(imageHandle.dataSize);
pageClient().saveImageToLibrary(WTFMove(buffer));
}
void WebPageProxy::applicationDidEnterBackground()
{
m_lastObservedStateWasBackground = true;
bool isSuspendedUnderLock = [UIApp isSuspendedUnderLock];
WEBPAGEPROXY_RELEASE_LOG(ViewState, "applicationDidEnterBackground: isSuspendedUnderLock? %d", isSuspendedUnderLock);
#if !PLATFORM(WATCHOS)
// We normally delay process suspension when the app is backgrounded until the current page load completes. However,
// we do not want to do so when the screen is locked for power reasons.
if (isSuspendedUnderLock)
NavigationState::fromWebPage(*this).releaseNetworkActivity(NavigationState::NetworkActivityReleaseReason::ScreenLocked);
#endif
m_process->send(Messages::WebPage::ApplicationDidEnterBackground(isSuspendedUnderLock), m_webPageID);
}
void WebPageProxy::applicationDidFinishSnapshottingAfterEnteringBackground()
{
if (m_drawingArea) {
m_drawingArea->prepareForAppSuspension();
m_drawingArea->hideContentUntilPendingUpdate();
}
m_process->send(Messages::WebPage::ApplicationDidFinishSnapshottingAfterEnteringBackground(), m_webPageID);
}
bool WebPageProxy::isInHardwareKeyboardMode()
{
return [UIKeyboard isInHardwareKeyboardMode];
}
void WebPageProxy::applicationWillEnterForeground()
{
m_lastObservedStateWasBackground = false;
bool isSuspendedUnderLock = [UIApp isSuspendedUnderLock];
WEBPAGEPROXY_RELEASE_LOG(ViewState, "applicationWillEnterForeground: isSuspendedUnderLock? %d", isSuspendedUnderLock);
m_process->send(Messages::WebPage::ApplicationWillEnterForeground(isSuspendedUnderLock), m_webPageID);
m_process->send(Messages::WebPage::HardwareKeyboardAvailabilityChanged(isInHardwareKeyboardMode()), m_webPageID);
}
void WebPageProxy::applicationWillResignActive()
{
m_process->send(Messages::WebPage::ApplicationWillResignActive(), m_webPageID);
}
void WebPageProxy::applicationDidEnterBackgroundForMedia()
{
bool isSuspendedUnderLock = [UIApp isSuspendedUnderLock];
WEBPAGEPROXY_RELEASE_LOG(ViewState, "applicationWillEnterForegroundForMedia: isSuspendedUnderLock? %d", isSuspendedUnderLock);
m_process->send(Messages::WebPage::ApplicationDidEnterBackgroundForMedia(isSuspendedUnderLock), m_webPageID);
}
void WebPageProxy::applicationWillEnterForegroundForMedia()
{
bool isSuspendedUnderLock = [UIApp isSuspendedUnderLock];
WEBPAGEPROXY_RELEASE_LOG(ViewState, "applicationDidEnterBackgroundForMedia: isSuspendedUnderLock? %d", isSuspendedUnderLock);
m_process->send(Messages::WebPage::ApplicationWillEnterForegroundForMedia(isSuspendedUnderLock), m_webPageID);
}
void WebPageProxy::applicationDidBecomeActive()
{
#if ENABLE(VIDEO_PRESENTATION_MODE)
if (m_videoFullscreenManager)
m_videoFullscreenManager->applicationDidBecomeActive();
#endif
m_process->send(Messages::WebPage::ApplicationDidBecomeActive(), m_webPageID);
}
void WebPageProxy::extendSelection(WebCore::TextGranularity granularity, CompletionHandler<void()>&& completionHandler)
{
sendWithAsyncReply(Messages::WebPage::ExtendSelection(granularity), [completionHandler = WTFMove(completionHandler)]() mutable {
if (completionHandler)
completionHandler();
});
}
void WebPageProxy::selectWordBackward()
{
m_process->send(Messages::WebPage::SelectWordBackward(), m_webPageID);
}
void WebPageProxy::requestRectsForGranularityWithSelectionOffset(WebCore::TextGranularity granularity, uint32_t offset, CompletionHandler<void(const Vector<WebCore::SelectionGeometry>&)>&& callback)
{
if (!hasRunningProcess())
return callback({ });
sendWithAsyncReply(Messages::WebPage::GetRectsForGranularityWithSelectionOffset(granularity, offset), WTFMove(callback));
}
void WebPageProxy::requestRectsAtSelectionOffsetWithText(int32_t offset, const String& text, CompletionHandler<void(const Vector<WebCore::SelectionGeometry>&)>&& callback)
{
if (!hasRunningProcess())
return callback({ });
sendWithAsyncReply(Messages::WebPage::GetRectsAtSelectionOffsetWithText(offset, text), WTFMove(callback));
}
void WebPageProxy::storeSelectionForAccessibility(bool shouldStore)
{
m_process->send(Messages::WebPage::StoreSelectionForAccessibility(shouldStore), m_webPageID);
}
void WebPageProxy::startAutoscrollAtPosition(const WebCore::FloatPoint& positionInWindow)
{
m_process->send(Messages::WebPage::StartAutoscrollAtPosition(positionInWindow), m_webPageID);
}
void WebPageProxy::cancelAutoscroll()
{
m_process->send(Messages::WebPage::CancelAutoscroll(), m_webPageID);
}
void WebPageProxy::moveSelectionByOffset(int32_t offset, CompletionHandler<void()>&& callbackFunction)
{
if (!hasRunningProcess()) {
callbackFunction();
return;
}
sendWithAsyncReply(Messages::WebPage::MoveSelectionByOffset(offset), [callbackFunction = WTFMove(callbackFunction), backgroundActivity = m_process->throttler().backgroundActivity("WebPageProxy::moveSelectionByOffset"_s)] () mutable {
callbackFunction();
});
}
void WebPageProxy::interpretKeyEvent(const EditorState& state, bool isCharEvent, CompletionHandler<void(bool)>&& completionHandler)
{
m_editorState = state;
if (m_keyEventQueue.isEmpty())
completionHandler(false);
else
completionHandler(pageClient().interpretKeyEvent(m_keyEventQueue.first(), isCharEvent));
}
void WebPageProxy::setSmartInsertDeleteEnabled(bool)
{
notImplemented();
}
void WebPageProxy::registerWebProcessAccessibilityToken(const IPC::DataReference& data)
{
pageClient().accessibilityWebProcessTokenReceived(data);
}
void WebPageProxy::assistiveTechnologyMakeFirstResponder()
{
pageClient().assistiveTechnologyMakeFirstResponder();
}
void WebPageProxy::makeFirstResponder()
{
notImplemented();
}
void WebPageProxy::registerUIProcessAccessibilityTokens(const IPC::DataReference& elementToken, const IPC::DataReference& windowToken)
{
if (!hasRunningProcess())
return;
send(Messages::WebPage::RegisterUIProcessAccessibilityTokens(elementToken, windowToken));
}
void WebPageProxy::executeSavedCommandBySelector(const String&, CompletionHandler<void(bool)>&& completionHandler)
{
notImplemented();
completionHandler(false);
}
bool WebPageProxy::shouldDelayWindowOrderingForEvent(const WebKit::WebMouseEvent&)
{
notImplemented();
return false;
}
void WebPageProxy::willStartUserTriggeredZooming()
{
send(Messages::WebPage::WillStartUserTriggeredZooming());
}
void WebPageProxy::potentialTapAtPosition(const WebCore::FloatPoint& position, bool shouldRequestMagnificationInformation, WebKit::TapIdentifier requestID)
{
hideValidationMessage();
send(Messages::WebPage::PotentialTapAtPosition(requestID, position, shouldRequestMagnificationInformation));
}
void WebPageProxy::commitPotentialTap(OptionSet<WebEvent::Modifier> modifiers, TransactionID layerTreeTransactionIdAtLastTouchStart, WebCore::PointerID pointerId)
{
send(Messages::WebPage::CommitPotentialTap(modifiers, layerTreeTransactionIdAtLastTouchStart, pointerId));
}
void WebPageProxy::cancelPotentialTap()
{
send(Messages::WebPage::CancelPotentialTap());
}
void WebPageProxy::tapHighlightAtPosition(const WebCore::FloatPoint& position, WebKit::TapIdentifier requestID)
{
send(Messages::WebPage::TapHighlightAtPosition(requestID, position));
}
void WebPageProxy::attemptSyntheticClick(const FloatPoint& location, OptionSet<WebEvent::Modifier> modifiers, TransactionID layerTreeTransactionIdAtLastTouchStart)
{
send(Messages::WebPage::AttemptSyntheticClick(roundedIntPoint(location), modifiers, layerTreeTransactionIdAtLastTouchStart));
}
void WebPageProxy::didRecognizeLongPress()
{
send(Messages::WebPage::DidRecognizeLongPress());
}
void WebPageProxy::handleDoubleTapForDoubleClickAtPoint(const WebCore::IntPoint& point, OptionSet<WebEvent::Modifier> modifiers, TransactionID layerTreeTransactionIdAtLastTouchStart)
{
send(Messages::WebPage::HandleDoubleTapForDoubleClickAtPoint(point, modifiers, layerTreeTransactionIdAtLastTouchStart));
}
void WebPageProxy::inspectorNodeSearchMovedToPosition(const WebCore::FloatPoint& position)
{
send(Messages::WebPage::InspectorNodeSearchMovedToPosition(position));
}
void WebPageProxy::inspectorNodeSearchEndedAtPosition(const WebCore::FloatPoint& position)
{
send(Messages::WebPage::InspectorNodeSearchEndedAtPosition(position));
}
void WebPageProxy::blurFocusedElement()
{
send(Messages::WebPage::BlurFocusedElement());
}
FloatSize WebPageProxy::screenSize()
{
return WebCore::screenSize();
}
FloatSize WebPageProxy::availableScreenSize()
{
return WebCore::availableScreenSize();
}
FloatSize WebPageProxy::overrideScreenSize()
{
return WebCore::overrideScreenSize();
}
float WebPageProxy::textAutosizingWidth()
{
return WebCore::screenSize().width();
}
void WebPageProxy::couldNotRestorePageState()
{
pageClient().couldNotRestorePageState();
}
void WebPageProxy::restorePageState(std::optional<WebCore::FloatPoint> scrollPosition, const WebCore::FloatPoint& scrollOrigin, const WebCore::FloatBoxExtent& obscuredInsetsOnSave, double scale)
{
pageClient().restorePageState(scrollPosition, scrollOrigin, obscuredInsetsOnSave, scale);
}
void WebPageProxy::restorePageCenterAndScale(std::optional<WebCore::FloatPoint> center, double scale)
{
pageClient().restorePageCenterAndScale(center, scale);
}
void WebPageProxy::didGetTapHighlightGeometries(WebKit::TapIdentifier requestID, const WebCore::Color& color, const Vector<WebCore::FloatQuad>& highlightedQuads, const WebCore::IntSize& topLeftRadius, const WebCore::IntSize& topRightRadius, const WebCore::IntSize& bottomLeftRadius, const WebCore::IntSize& bottomRightRadius, bool nodeHasBuiltInClickHandling)
{
pageClient().didGetTapHighlightGeometries(requestID, color, highlightedQuads, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, nodeHasBuiltInClickHandling);
}
void WebPageProxy::setIsShowingInputViewForFocusedElement(bool showingInputView)
{
send(Messages::WebPage::SetIsShowingInputViewForFocusedElement(showingInputView));
}
void WebPageProxy::updateInputContextAfterBlurringAndRefocusingElement()
{
pageClient().updateInputContextAfterBlurringAndRefocusingElement();
}
void WebPageProxy::elementDidFocus(const FocusedElementInformation& information, bool userIsInteracting, bool blurPreviousNode, OptionSet<WebCore::ActivityState::Flag> activityStateChanges, const UserData& userData)
{
m_pendingInputModeChange = std::nullopt;
API::Object* userDataObject = process().transformHandlesToObjects(userData.object()).get();
pageClient().elementDidFocus(information, userIsInteracting, blurPreviousNode, activityStateChanges, userDataObject);
}
void WebPageProxy::elementDidBlur()
{
m_pendingInputModeChange = std::nullopt;
pageClient().elementDidBlur();
}
void WebPageProxy::focusedElementDidChangeInputMode(WebCore::InputMode mode)
{
#if ENABLE(TOUCH_EVENTS)
if (m_touchAndPointerEventTracking.isTrackingAnything()) {
m_pendingInputModeChange = mode;
return;
}
#endif
pageClient().focusedElementDidChangeInputMode(mode);
}
void WebPageProxy::didReleaseAllTouchPoints()
{
if (!m_pendingInputModeChange)
return;
pageClient().focusedElementDidChangeInputMode(*m_pendingInputModeChange);
m_pendingInputModeChange = std::nullopt;
}
void WebPageProxy::autofillLoginCredentials(const String& username, const String& password)
{
m_process->send(Messages::WebPage::AutofillLoginCredentials(username, password), m_webPageID);
}
void WebPageProxy::showInspectorHighlight(const WebCore::InspectorOverlay::Highlight& highlight)
{
pageClient().showInspectorHighlight(highlight);
}
void WebPageProxy::hideInspectorHighlight()
{
pageClient().hideInspectorHighlight();
}
void WebPageProxy::showInspectorIndication()
{
pageClient().showInspectorIndication();
}
void WebPageProxy::hideInspectorIndication()
{
pageClient().hideInspectorIndication();
}
void WebPageProxy::enableInspectorNodeSearch()
{
pageClient().enableInspectorNodeSearch();
}
void WebPageProxy::disableInspectorNodeSearch()
{
pageClient().disableInspectorNodeSearch();
}
void WebPageProxy::focusNextFocusedElement(bool isForward, CompletionHandler<void()>&& callbackFunction)
{
if (!hasRunningProcess()) {
callbackFunction();
return;
}
sendWithAsyncReply(Messages::WebPage::FocusNextFocusedElement(isForward), [callbackFunction = WTFMove(callbackFunction), backgroundActivity = m_process->throttler().backgroundActivity("WebPageProxy::focusNextFocusedElement"_s)] () mutable {
callbackFunction();
});
}
void WebPageProxy::setFocusedElementValue(const WebCore::ElementContext& context, const String& value)
{
send(Messages::WebPage::SetFocusedElementValue(context, value));
}
void WebPageProxy::setFocusedElementSelectedIndex(const WebCore::ElementContext& context, uint32_t index, bool allowMultipleSelection)
{
send(Messages::WebPage::SetFocusedElementSelectedIndex(context, index, allowMultipleSelection));
}
void WebPageProxy::didPerformDictionaryLookup(const DictionaryPopupInfo& dictionaryPopupInfo)
{
pageClient().didPerformDictionaryLookup(dictionaryPopupInfo);
}
void WebPageProxy::setRemoteLayerTreeRootNode(RemoteLayerTreeNode* rootNode)
{
pageClient().setRemoteLayerTreeRootNode(rootNode);
m_frozenRemoteLayerTreeHost = nullptr;
}
void WebPageProxy::showPlaybackTargetPicker(bool hasVideo, const IntRect& elementRect, WebCore::RouteSharingPolicy policy, const String& contextUID)
{
pageClient().showPlaybackTargetPicker(hasVideo, elementRect, policy, contextUID);
}
void WebPageProxy::commitPotentialTapFailed()
{
pageClient().commitPotentialTapFailed();
}
void WebPageProxy::didNotHandleTapAsClick(const WebCore::IntPoint& point)
{
pageClient().didNotHandleTapAsClick(point);
m_uiClient->didNotHandleTapAsClick(point);
}
void WebPageProxy::didCompleteSyntheticClick()
{
pageClient().didCompleteSyntheticClick();
}
void WebPageProxy::disableDoubleTapGesturesDuringTapIfNecessary(WebKit::TapIdentifier requestID)
{
pageClient().disableDoubleTapGesturesDuringTapIfNecessary(requestID);
}
void WebPageProxy::handleSmartMagnificationInformationForPotentialTap(WebKit::TapIdentifier requestID, const WebCore::FloatRect& renderRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale, bool nodeIsRootLevel)
{
pageClient().handleSmartMagnificationInformationForPotentialTap(requestID, renderRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale, nodeIsRootLevel);
}
std::pair<size_t, uint64_t> WebPageProxy::computePagesForPrintingAndDrawToPDF(FrameIdentifier frameID, const PrintInfo& printInfo, CompletionHandler<void(const IPC::SharedBufferCopy&)>&& callback)
{
if (!hasRunningProcess()) {
callback({ });
return { 0, 0 };
}
size_t pageCount = 0;
if (printInfo.snapshotFirstPage)
pageCount = 1;
else
sendSync(Messages::WebPage::ComputePagesForPrintingiOS(frameID, printInfo), Messages::WebPage::ComputePagesForPrintingiOS::Reply(pageCount), Seconds::infinity());
auto callbackID = sendWithAsyncReply(Messages::WebPage::DrawToPDFiOS(frameID, printInfo, pageCount), WTFMove(callback));
return { pageCount, callbackID };
}
void WebPageProxy::contentSizeCategoryDidChange(const String& contentSizeCategory)
{
send(Messages::WebPage::ContentSizeCategoryDidChange(contentSizeCategory));
}
void WebPageProxy::generateSyntheticEditingCommand(WebKit::SyntheticEditingCommandType command)
{
if (!hasRunningProcess())
return;
send(Messages::WebPage::GenerateSyntheticEditingCommand(command));
}
void WebPageProxy::didUpdateEditorState(const EditorState& oldEditorState, const EditorState& newEditorState)
{
bool couldChangeSecureInputState = newEditorState.isInPasswordField != oldEditorState.isInPasswordField || oldEditorState.selectionIsNone;
// Selection being none is a temporary state when editing. Flipping secure input state too quickly was causing trouble (not fully understood).
if (couldChangeSecureInputState && !newEditorState.selectionIsNone)
pageClient().updateSecureInputState();
if (newEditorState.shouldIgnoreSelectionChanges)
return;
updateFontAttributesAfterEditorStateChange();
// We always need to notify the client on iOS to make sure the selection is redrawn,
// even during composition to support phrase boundary gesture.
pageClient().selectionDidChange();
}
void WebPageProxy::dispatchDidUpdateEditorState()
{
if (!m_waitingForPostLayoutEditorStateUpdateAfterFocusingElement || m_editorState.isMissingPostLayoutData)
return;
pageClient().didUpdateEditorState();
m_waitingForPostLayoutEditorStateUpdateAfterFocusingElement = false;
}
void WebPageProxy::showValidationMessage(const IntRect& anchorClientRect, const String& message)
{
m_validationBubble = pageClient().createValidationBubble(message, { m_preferences->minimumFontSize() });
m_validationBubble->setAnchorRect(anchorClientRect, uiClient().presentingViewController());
// If we are currently doing a scrolling / zoom animation, then we'll delay showing the validation
// bubble until the animation is over.
if (!m_isScrollingOrZooming)
m_validationBubble->show();
}
void WebPageProxy::setIsScrollingOrZooming(bool isScrollingOrZooming)
{
m_isScrollingOrZooming = isScrollingOrZooming;
// We finished doing the scrolling / zoom animation so we can now show the validation
// bubble if we're supposed to.
if (!m_isScrollingOrZooming && m_validationBubble)
m_validationBubble->show();
}
void WebPageProxy::hardwareKeyboardAvailabilityChanged(bool keyboardIsAttached)
{
updateCurrentModifierState();
m_process->send(Messages::WebPage::HardwareKeyboardAvailabilityChanged(keyboardIsAttached), m_webPageID);
}
void WebPageProxy::requestEvasionRectsAboveSelection(CompletionHandler<void(const Vector<WebCore::FloatRect>&)>&& callback)
{
if (!hasRunningProcess()) {
callback({ });
return;
}
sendWithAsyncReply(Messages::WebPage::RequestEvasionRectsAboveSelection(), WTFMove(callback));
}
void WebPageProxy::updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&& completionHandler)
{
if (!hasRunningProcess()) {
completionHandler();
return;
}
sendWithAsyncReply(Messages::WebPage::UpdateSelectionWithDelta(locationDelta, lengthDelta), WTFMove(completionHandler));
}
WebCore::FloatRect WebPageProxy::selectionBoundingRectInRootViewCoordinates() const
{
if (m_editorState.selectionIsNone)
return { };
if (m_editorState.isMissingPostLayoutData)
return { };
WebCore::FloatRect bounds;
auto& postLayoutData = m_editorState.postLayoutData();
if (m_editorState.selectionIsRange) {
for (auto& geometry : postLayoutData.selectionGeometries)
bounds.unite(geometry.rect());
} else
bounds = postLayoutData.caretRectAtStart;
return bounds;
}
void WebPageProxy::requestDocumentEditingContext(WebKit::DocumentEditingContextRequest request, CompletionHandler<void(WebKit::DocumentEditingContext)>&& completionHandler)
{
if (!hasRunningProcess()) {
completionHandler({ });
return;
}
sendWithAsyncReply(Messages::WebPage::RequestDocumentEditingContext(request), WTFMove(completionHandler));
}
#if ENABLE(DRAG_SUPPORT)
void WebPageProxy::didHandleDragStartRequest(bool started)
{
pageClient().didHandleDragStartRequest(started);
}
void WebPageProxy::didHandleAdditionalDragItemsRequest(bool added)
{
pageClient().didHandleAdditionalDragItemsRequest(added);
}
void WebPageProxy::requestDragStart(const WebCore::IntPoint& clientPosition, const WebCore::IntPoint& globalPosition, OptionSet<WebCore::DragSourceAction> allowedActionsMask)
{
if (hasRunningProcess())
m_process->send(Messages::WebPage::RequestDragStart(clientPosition, globalPosition, allowedActionsMask), m_webPageID);
}
void WebPageProxy::requestAdditionalItemsForDragSession(const IntPoint& clientPosition, const IntPoint& globalPosition, OptionSet<WebCore::DragSourceAction> allowedActionsMask)
{
if (hasRunningProcess())
m_process->send(Messages::WebPage::RequestAdditionalItemsForDragSession(clientPosition, globalPosition, allowedActionsMask), m_webPageID);
}
void WebPageProxy::insertDroppedImagePlaceholders(const Vector<IntSize>& imageSizes, CompletionHandler<void(const Vector<IntRect>&, std::optional<WebCore::TextIndicatorData>)>&& completionHandler)
{
if (hasRunningProcess())
sendWithAsyncReply(Messages::WebPage::InsertDroppedImagePlaceholders(imageSizes), WTFMove(completionHandler));
else
completionHandler({ }, std::nullopt);
}
void WebPageProxy::willReceiveEditDragSnapshot()
{
pageClient().willReceiveEditDragSnapshot();
}
void WebPageProxy::didReceiveEditDragSnapshot(std::optional<TextIndicatorData> data)
{
pageClient().didReceiveEditDragSnapshot(data);
}
void WebPageProxy::didConcludeDrop()
{
m_process->send(Messages::WebPage::DidConcludeDrop(), m_webPageID);
}
#endif
#if USE(QUICK_LOOK)
void WebPageProxy::didStartLoadForQuickLookDocumentInMainFrame(const String& fileName, const String& uti)
{
// Ensure that fileName isn't really a path name
static_assert(notFound + 1 == 0, "The following line assumes WTF::notFound equals -1");
m_navigationClient->didStartLoadForQuickLookDocumentInMainFrame(fileName.substring(fileName.reverseFind('/') + 1), uti);
}
void WebPageProxy::didFinishLoadForQuickLookDocumentInMainFrame(const ShareableResource::Handle& handle)
{
auto buffer = handle.tryWrapInSharedBuffer();
if (!buffer)
return;
m_navigationClient->didFinishLoadForQuickLookDocumentInMainFrame(*buffer);
}
void WebPageProxy::requestPasswordForQuickLookDocumentInMainFrame(const String& fileName, CompletionHandler<void(const String&)>&& completionHandler)
{
requestPasswordForQuickLookDocumentInMainFrameShared(fileName, WTFMove(completionHandler));
}
void WebPageProxy::requestPasswordForQuickLookDocumentInMainFrameShared(const String& fileName, CompletionHandler<void(const String&)>&& completionHandler)
{
pageClient().requestPasswordForQuickLookDocument(fileName, WTFMove(completionHandler));
}
#endif
#if ENABLE(APPLE_PAY)
std::unique_ptr<PaymentAuthorizationPresenter> WebPageProxy::paymentCoordinatorAuthorizationPresenter(WebPaymentCoordinatorProxy& paymentCoordinatorProxy, PKPaymentRequest *paymentRequest)
{
return makeUnique<PaymentAuthorizationViewController>(paymentCoordinatorProxy, paymentRequest);
}
UIViewController *WebPageProxy::paymentCoordinatorPresentingViewController(const WebPaymentCoordinatorProxy&)
{
return uiClient().presentingViewController();
}
#if ENABLE(APPLE_PAY_REMOTE_UI_USES_SCENE)
void WebPageProxy::getWindowSceneIdentifierForPaymentPresentation(WebPageProxyIdentifier, CompletionHandler<void(const String&)>&& completionHandler)
{
ASSERT_NOT_REACHED();
completionHandler(nullString());
}
#endif
const String& WebPageProxy::paymentCoordinatorCTDataConnectionServiceType(const WebPaymentCoordinatorProxy&)
{
return websiteDataStore().configuration().dataConnectionServiceType();
}
#endif // ENABLE(APPLE_PAY)
static bool desktopClassBrowsingSupported()
{
static bool supportsDesktopClassBrowsing = false;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
supportsDesktopClassBrowsing = !currentUserInterfaceIdiomIsSmallScreen();
});
return supportsDesktopClassBrowsing;
}
#if !PLATFORM(MACCATALYST)
static bool webViewSizeIsNarrow(WebCore::IntSize viewSize)
{
return !viewSize.isEmpty() && viewSize.width() <= 375;
}
#endif // !PLATFORM(MACCATALYST)
enum class RecommendDesktopClassBrowsingForRequest { No, Yes, Auto };
static RecommendDesktopClassBrowsingForRequest desktopClassBrowsingRecommendedForRequest(const WebCore::ResourceRequest& request)
{
// FIXME: This should be additionally gated on site-specific quirks being enabled. However, site-specific quirks are already
// disabled by default in WKWebView, so we would need a new preference for controlling site-specific quirks that are on-by-default
// in all apps, but may be turned off via SPI (or via Web Inspector). See also: <rdar://problem/50035167>.
auto host = request.url().host();
if (equalLettersIgnoringASCIICase(host, "tv.kakao.com") || host.endsWithIgnoringASCIICase(".tv.kakao.com"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "tving.com") || host.endsWithIgnoringASCIICase(".tving.com"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "live.iqiyi.com") || host.endsWithIgnoringASCIICase(".live.iqiyi.com"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "jsfiddle.net") || host.endsWithIgnoringASCIICase(".jsfiddle.net"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "video.sina.com.cn") || host.endsWithIgnoringASCIICase(".video.sina.com.cn"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "huya.com") || host.endsWithIgnoringASCIICase(".huya.com"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "video.tudou.com") || host.endsWithIgnoringASCIICase(".video.tudou.com"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "cctv.com") || host.endsWithIgnoringASCIICase(".cctv.com"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "v.china.com.cn"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "trello.com") || host.endsWithIgnoringASCIICase(".trello.com"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "ted.com") || host.endsWithIgnoringASCIICase(".ted.com"))
return RecommendDesktopClassBrowsingForRequest::No;
if (host.containsIgnoringASCIICase("hsbc.")) {
if (equalLettersIgnoringASCIICase(host, "hsbc.com.au") || host.endsWithIgnoringASCIICase(".hsbc.com.au"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.com.eg") || host.endsWithIgnoringASCIICase(".hsbc.com.eg"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.lk") || host.endsWithIgnoringASCIICase(".hsbc.lk"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.co.uk") || host.endsWithIgnoringASCIICase(".hsbc.co.uk"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.com.hk") || host.endsWithIgnoringASCIICase(".hsbc.com.hk"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.com.mx") || host.endsWithIgnoringASCIICase(".hsbc.com.mx"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.ca") || host.endsWithIgnoringASCIICase(".hsbc.ca"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.com.ar") || host.endsWithIgnoringASCIICase(".hsbc.com.ar"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.com.ph") || host.endsWithIgnoringASCIICase(".hsbc.com.ph"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.com") || host.endsWithIgnoringASCIICase(".hsbc.com"))
return RecommendDesktopClassBrowsingForRequest::No;
if (equalLettersIgnoringASCIICase(host, "hsbc.com.cn") || host.endsWithIgnoringASCIICase(".hsbc.com.cn"))
return RecommendDesktopClassBrowsingForRequest::No;
}
if (equalLettersIgnoringASCIICase(host, "nhl.com") || host.endsWithIgnoringASCIICase(".nhl.com"))
return RecommendDesktopClassBrowsingForRequest::No;
// FIXME: Remove this quirk when <rdar://problem/59480381> is complete.
if (equalLettersIgnoringASCIICase(host, "fidelity.com") || host.endsWithIgnoringASCIICase(".fidelity.com"))
return RecommendDesktopClassBrowsingForRequest::No;
// FIXME: Remove this quirk when <rdar://problem/61733101> is complete.
if (equalLettersIgnoringASCIICase(host, "roblox.com") || host.endsWithIgnoringASCIICase(".roblox.com"))
return RecommendDesktopClassBrowsingForRequest::No;
return RecommendDesktopClassBrowsingForRequest::Auto;
}
enum class IgnoreAppCompatibilitySafeguards : bool { No, Yes };
static bool desktopClassBrowsingRecommended(const WebCore::ResourceRequest& request, WebCore::IntSize viewSize, IgnoreAppCompatibilitySafeguards ignoreSafeguards)
{
auto desktopClassBrowsingRecommendation = desktopClassBrowsingRecommendedForRequest(request);
if (desktopClassBrowsingRecommendation == RecommendDesktopClassBrowsingForRequest::Yes)
return true;
if (desktopClassBrowsingRecommendation == RecommendDesktopClassBrowsingForRequest::No)
return false;
#if !PLATFORM(MACCATALYST)
if (webViewSizeIsNarrow(viewSize))
return false;
#endif
static bool shouldRecommendDesktopClassBrowsing = false;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if PLATFORM(MACCATALYST)
shouldRecommendDesktopClassBrowsing = desktopClassBrowsingSupported();
#else
// While desktop-class browsing is supported on all iPad models, it is not recommended for iPad mini.
auto screenClass = MGGetSInt32Answer(kMGQMainScreenClass, MGScreenClassPad2);
shouldRecommendDesktopClassBrowsing = screenClass != MGScreenClassPad3 && screenClass != MGScreenClassPad4 && desktopClassBrowsingSupported();
#endif
if (ignoreSafeguards == IgnoreAppCompatibilitySafeguards::No && !linkedOnOrAfter(SDKVersion::FirstWithModernCompabilityModeByDefault)) {
// Opt out apps that haven't yet built against the iOS 13 SDK to limit any incompatibilities as a result of enabling desktop-class browsing by default in
// WKWebView on appropriately-sized iPad models.
shouldRecommendDesktopClassBrowsing = false;
}
});
return shouldRecommendDesktopClassBrowsing;
}
WebContentMode WebPageProxy::effectiveContentModeAfterAdjustingPolicies(API::WebsitePolicies& policies, const WebCore::ResourceRequest& request)
{
if (m_preferences->mediaSourceEnabled()) {
// FIXME: This is a compatibility hack to ensure that turning MSE on via the existing preference still enables MSE.
policies.setMediaSourcePolicy(WebsiteMediaSourcePolicy::Enable);
}
auto viewSize = this->viewSize();
bool useDesktopBrowsingMode;
switch (policies.preferredContentMode()) {
case WebContentMode::Recommended: {
auto ignoreSafeguards = m_navigationClient->shouldBypassContentModeSafeguards() ? IgnoreAppCompatibilitySafeguards::Yes : IgnoreAppCompatibilitySafeguards::No;
useDesktopBrowsingMode = desktopClassBrowsingRecommended(request, viewSize, ignoreSafeguards);
break;
}
case WebContentMode::Mobile:
useDesktopBrowsingMode = false;
break;
case WebContentMode::Desktop:
useDesktopBrowsingMode = !policies.allowSiteSpecificQuirksToOverrideContentMode() || desktopClassBrowsingRecommendedForRequest(request) != RecommendDesktopClassBrowsingForRequest::No;
break;
default:
ASSERT_NOT_REACHED();
useDesktopBrowsingMode = false;
break;
}
m_preferFasterClickOverDoubleTap = false;
if (!useDesktopBrowsingMode) {
policies.setAllowContentChangeObserverQuirk(true);
policies.setIdempotentModeAutosizingOnlyHonorsPercentages(true);
return WebContentMode::Mobile;
}
if (policies.customUserAgent().isEmpty() && customUserAgent().isEmpty()) {
auto applicationName = policies.applicationNameForDesktopUserAgent();
if (applicationName.isEmpty())
applicationName = applicationNameForDesktopUserAgent();
policies.setCustomUserAgent(standardUserAgentWithApplicationName(applicationName, emptyString(), UserAgentType::Desktop));
}
if (policies.customNavigatorPlatform().isEmpty()) {
// FIXME: Grab this from WebCore instead of hard-coding it here.
policies.setCustomNavigatorPlatform("MacIntel"_s);
}
if (desktopClassBrowsingSupported()) {
// Apply some additional desktop-class browsing behaviors on supported devices.
policies.setMetaViewportPolicy(WebsiteMetaViewportPolicy::Ignore);
policies.setMediaSourcePolicy(WebsiteMediaSourcePolicy::Enable);
policies.setSimulatedMouseEventsDispatchPolicy(WebsiteSimulatedMouseEventsDispatchPolicy::Allow);
policies.setLegacyOverflowScrollingTouchPolicy(WebsiteLegacyOverflowScrollingTouchPolicy::Disable);
m_preferFasterClickOverDoubleTap = true;
}
return WebContentMode::Desktop;
}
bool WebPageProxy::shouldForceForegroundPriorityForClientNavigation() const
{
// The client may request that we do client navigations at foreground priority, even if the
// view is not visible, as long as the application is foreground.
if (!configuration().clientNavigationsRunAtForegroundPriority())
return false;
// This setting only applies to background views. There is no need to force foreground
// priority for foreground views since they get foreground priority by virtue of being
// visible.
if (isViewVisible())
return false;
bool canTakeForegroundAssertions = pageClient().canTakeForegroundAssertions();
WEBPAGEPROXY_RELEASE_LOG(Process, "WebPageProxy::shouldForceForegroundPriorityForClientNavigation() returns %d based on PageClient::canTakeForegroundAssertions()", canTakeForegroundAssertions);
return canTakeForegroundAssertions;
}
void WebPageProxy::textInputContextsInRect(FloatRect rect, CompletionHandler<void(const Vector<ElementContext>&)>&& completionHandler)
{
if (!hasRunningProcess()) {
completionHandler({ });
return;
}
sendWithAsyncReply(Messages::WebPage::TextInputContextsInRect(rect), WTFMove(completionHandler));
}
void WebPageProxy::focusTextInputContextAndPlaceCaret(const ElementContext& context, const IntPoint& point, CompletionHandler<void(bool)>&& completionHandler)
{
if (!hasRunningProcess()) {
completionHandler(false);
return;
}
sendWithAsyncReply(Messages::WebPage::FocusTextInputContextAndPlaceCaret(context, point), WTFMove(completionHandler));
}
void WebPageProxy::setShouldRevealCurrentSelectionAfterInsertion(bool shouldRevealCurrentSelectionAfterInsertion)
{
if (hasRunningProcess())
send(Messages::WebPage::SetShouldRevealCurrentSelectionAfterInsertion(shouldRevealCurrentSelectionAfterInsertion));
}
void WebPageProxy::setScreenIsBeingCaptured(bool captured)
{
if (hasRunningProcess())
send(Messages::WebPage::SetScreenIsBeingCaptured(captured));
}
void WebPageProxy::willOpenAppLink()
{
if (m_openingAppLinkActivity && m_openingAppLinkActivity->isValid())
return;
// We take a background activity for 25 seconds when switching to another app via an app link in case the WebPage
// needs to run script to pass information to the native app.
// We chose 25 seconds because the system only gives us 30 seconds and we don't want to get too close to that limit
// to avoid assertion invalidation (or even termination).
m_openingAppLinkActivity = process().throttler().backgroundActivity("Opening AppLink"_s).moveToUniquePtr();
WorkQueue::main().dispatchAfter(25_s, [weakThis = WeakPtr { *this }] {
if (weakThis)
weakThis->m_openingAppLinkActivity = nullptr;
});
}
void WebPageProxy::processWillBecomeSuspended()
{
if (!hasRunningProcess())
return;
m_hasNetworkRequestsOnSuspended = m_pageLoadState.networkRequestsInProgress();
if (m_hasNetworkRequestsOnSuspended)
setNetworkRequestsInProgress(false);
}
void WebPageProxy::processWillBecomeForeground()
{
if (!hasRunningProcess())
return;
if (m_hasNetworkRequestsOnSuspended) {
setNetworkRequestsInProgress(true);
m_hasNetworkRequestsOnSuspended = false;
}
}
#if PLATFORM(MACCATALYST)
void WebPageProxy::isUserFacingChanged(bool isUserFacing)
{
if (!isUserFacing)
suspendAllMediaPlayback([] { });
else
resumeAllMediaPlayback([] { });
}
#endif
void WebPageProxy::willPerformPasteCommand(DOMPasteAccessCategory pasteAccessCategory)
{
switch (pasteAccessCategory) {
case DOMPasteAccessCategory::General:
case DOMPasteAccessCategory::Fonts:
grantAccessToCurrentPasteboardData(UIPasteboardNameGeneral);
return;
}
}
void WebPageProxy::setDeviceHasAGXCompilerServiceForTesting() const
{
WebCore::setDeviceHasAGXCompilerServiceForTesting();
}
void WebPageProxy::showDataDetectorsUIForPositionInformation(const InteractionInformationAtPosition& positionInfo)
{
pageClient().showDataDetectorsUIForPositionInformation(positionInfo);
}
Color WebPageProxy::platformUnderPageBackgroundColor() const
{
if (auto contentViewBackgroundColor = pageClient().contentViewBackgroundColor(); contentViewBackgroundColor.isValid())
return contentViewBackgroundColor;
#if HAVE(OS_DARK_MODE_SUPPORT)
return WebCore::roundAndClampToSRGBALossy(UIColor.systemBackgroundColor.CGColor);
#else
return WebCore::Color::white;
#endif
}
} // namespace WebKit
#undef WEBPAGEPROXY_RELEASE_LOG
#undef MESSAGE_CHECK
#endif // PLATFORM(IOS_FAMILY)