| /* |
| * Copyright (C) 2010-2022 Apple Inc. All rights reserved. |
| * Copyright (C) 2012 Intel Corporation. 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. |
| */ |
| |
| #include "config.h" |
| #include "WebPageProxy.h" |
| |
| #include "APIArray.h" |
| #include "APIAttachment.h" |
| #include "APIContentWorld.h" |
| #include "APIContextMenuClient.h" |
| #include "APIDictionary.h" |
| #include "APIFindClient.h" |
| #include "APIFindMatchesClient.h" |
| #include "APIFormClient.h" |
| #include "APIFrameInfo.h" |
| #include "APIFullscreenClient.h" |
| #include "APIGeometry.h" |
| #include "APIHistoryClient.h" |
| #include "APIHitTestResult.h" |
| #include "APIIconLoadingClient.h" |
| #include "APILegacyContextHistoryClient.h" |
| #include "APILoaderClient.h" |
| #include "APINavigation.h" |
| #include "APINavigationAction.h" |
| #include "APINavigationClient.h" |
| #include "APINavigationResponse.h" |
| #include "APIOpenPanelParameters.h" |
| #include "APIPageConfiguration.h" |
| #include "APIPolicyClient.h" |
| #include "APIResourceLoadClient.h" |
| #include "APISecurityOrigin.h" |
| #include "APIUIClient.h" |
| #include "APIURLRequest.h" |
| #include "APIWebsitePolicies.h" |
| #include "AuthenticationChallengeProxy.h" |
| #include "AuthenticationDecisionListener.h" |
| #include "AuthenticationManager.h" |
| #include "AuthenticatorManager.h" |
| #include "DownloadManager.h" |
| #include "DownloadProxy.h" |
| #include "DrawingAreaMessages.h" |
| #include "DrawingAreaProxy.h" |
| #include "EventDispatcherMessages.h" |
| #include "FormDataReference.h" |
| #include "FrameInfoData.h" |
| #include "LegacyGlobalSettings.h" |
| #include "LoadParameters.h" |
| #include "LogInitialization.h" |
| #include "Logging.h" |
| #include "NativeWebGestureEvent.h" |
| #include "NativeWebKeyboardEvent.h" |
| #include "NativeWebMouseEvent.h" |
| #include "NativeWebWheelEvent.h" |
| #include "NavigationActionData.h" |
| #include "NetworkProcessMessages.h" |
| #include "NetworkProcessProxy.h" |
| #include "NotificationManagerMessageHandlerMessages.h" |
| #include "NotificationPermissionRequest.h" |
| #include "NotificationPermissionRequestManager.h" |
| #include "PageClient.h" |
| #include "PrintInfo.h" |
| #include "ProcessThrottler.h" |
| #include "ProvisionalPageProxy.h" |
| #include "SafeBrowsingWarning.h" |
| #include "SpeechRecognitionPermissionManager.h" |
| #include "SpeechRecognitionRemoteRealtimeMediaSource.h" |
| #include "SpeechRecognitionRemoteRealtimeMediaSourceManager.h" |
| #include "SyntheticEditingCommandType.h" |
| #include "TextChecker.h" |
| #include "TextCheckerState.h" |
| #include "TextRecognitionUpdateResult.h" |
| #include "URLSchemeTaskParameters.h" |
| #include "UndoOrRedo.h" |
| #include "UserMediaPermissionRequestProxy.h" |
| #include "UserMediaProcessManager.h" |
| #include "WKContextPrivate.h" |
| #include "WebAutomationSession.h" |
| #include "WebBackForwardCache.h" |
| #include "WebBackForwardList.h" |
| #include "WebBackForwardListCounts.h" |
| #include "WebBackForwardListItem.h" |
| #include "WebCertificateInfo.h" |
| #include "WebContextMenuItem.h" |
| #include "WebContextMenuProxy.h" |
| #include "WebCoreArgumentCoders.h" |
| #include "WebEditCommandProxy.h" |
| #include "WebEventConversion.h" |
| #include "WebFoundTextRange.h" |
| #include "WebFrame.h" |
| #include "WebFramePolicyListenerProxy.h" |
| #include "WebFullScreenManagerProxy.h" |
| #include "WebFullScreenManagerProxyMessages.h" |
| #include "WebImage.h" |
| #include "WebInspectorUIProxy.h" |
| #include "WebInspectorUtilities.h" |
| #include "WebKeyboardEvent.h" |
| #include "WebNavigationDataStore.h" |
| #include "WebNavigationState.h" |
| #include "WebNotificationManagerProxy.h" |
| #include "WebOpenPanelResultListenerProxy.h" |
| #include "WebPage.h" |
| #include "WebPageCreationParameters.h" |
| #include "WebPageDebuggable.h" |
| #include "WebPageGroup.h" |
| #include "WebPageGroupData.h" |
| #include "WebPageInspectorController.h" |
| #include "WebPageMessages.h" |
| #include "WebPageNetworkParameters.h" |
| #include "WebPageProxyMessages.h" |
| #include "WebPasteboardProxy.h" |
| #include "WebPaymentCoordinatorProxy.h" |
| #include "WebPopupItem.h" |
| #include "WebPopupMenuProxy.h" |
| #include "WebPreferences.h" |
| #include "WebPreferencesKeys.h" |
| #include "WebProcess.h" |
| #include "WebProcessMessages.h" |
| #include "WebProcessPool.h" |
| #include "WebProcessProxy.h" |
| #include "WebProtectionSpace.h" |
| #include "WebResourceLoadStatisticsStore.h" |
| #include "WebURLSchemeHandler.h" |
| #include "WebUserContentControllerProxy.h" |
| #include "WebViewDidMoveToWindowObserver.h" |
| #include "WebWheelEventCoalescer.h" |
| #include "WebsiteDataStore.h" |
| #include <WebCore/AppHighlight.h> |
| #include <WebCore/BitmapImage.h> |
| #include <WebCore/CompositionHighlight.h> |
| #include <WebCore/CrossSiteNavigationDataTransfer.h> |
| #include <WebCore/DOMPasteAccess.h> |
| #include <WebCore/DeprecatedGlobalSettings.h> |
| #include <WebCore/DiagnosticLoggingClient.h> |
| #include <WebCore/DiagnosticLoggingKeys.h> |
| #include <WebCore/DragController.h> |
| #include <WebCore/DragData.h> |
| #include <WebCore/ElementContext.h> |
| #include <WebCore/EventNames.h> |
| #include <WebCore/ExceptionDetails.h> |
| #include <WebCore/FloatRect.h> |
| #include <WebCore/FocusDirection.h> |
| #include <WebCore/FontAttributeChanges.h> |
| #include <WebCore/FrameLoader.h> |
| #include <WebCore/GlobalFrameIdentifier.h> |
| #include <WebCore/GlobalWindowIdentifier.h> |
| #include <WebCore/LengthBox.h> |
| #include <WebCore/MIMETypeRegistry.h> |
| #include <WebCore/MediaStreamRequest.h> |
| #include <WebCore/ModalContainerTypes.h> |
| #include <WebCore/PerformanceLoggingClient.h> |
| #include <WebCore/PermissionState.h> |
| #include <WebCore/PlatformEvent.h> |
| #include <WebCore/PrivateClickMeasurement.h> |
| #include <WebCore/PublicSuffix.h> |
| #include <WebCore/RealtimeMediaSourceCenter.h> |
| #include <WebCore/RenderEmbeddedObject.h> |
| #include <WebCore/ResourceLoadStatistics.h> |
| #include <WebCore/RuntimeApplicationChecks.h> |
| #include <WebCore/SSLKeyGenerator.h> |
| #include <WebCore/SerializedCryptoKeyWrap.h> |
| #include <WebCore/ShareData.h> |
| #include <WebCore/SharedBuffer.h> |
| #include <WebCore/ShouldTreatAsContinuingLoad.h> |
| #include <WebCore/StoredCredentialsPolicy.h> |
| #include <WebCore/TextCheckerClient.h> |
| #include <WebCore/TextIndicator.h> |
| #include <WebCore/ValidationBubble.h> |
| #include <WebCore/WindowFeatures.h> |
| #include <WebCore/WritingDirection.h> |
| #include <stdio.h> |
| #include <wtf/CallbackAggregator.h> |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/Scope.h> |
| #include <wtf/SystemTracing.h> |
| #include <wtf/URL.h> |
| #include <wtf/URLParser.h> |
| #include <wtf/WeakPtr.h> |
| #include <wtf/text/StringView.h> |
| #include <wtf/text/TextStream.h> |
| |
| #if ENABLE(APPLICATION_MANIFEST) |
| #include "APIApplicationManifest.h" |
| #endif |
| |
| #if ENABLE(ASYNC_SCROLLING) && PLATFORM(COCOA) |
| #include "RemoteScrollingCoordinatorProxy.h" |
| #endif |
| |
| #ifndef NDEBUG |
| #include <wtf/RefCountedLeakCounter.h> |
| #endif |
| |
| #if PLATFORM(COCOA) |
| #include "InsertTextOptions.h" |
| #include "RemoteLayerTreeDrawingAreaProxy.h" |
| #include "RemoteLayerTreeScrollingPerformanceData.h" |
| #include "UserMediaCaptureManagerProxy.h" |
| #include "VideoFullscreenManagerProxy.h" |
| #include "VideoFullscreenManagerProxyMessages.h" |
| #include <WebCore/AttributedString.h> |
| #include <WebCore/CoreAudioCaptureDeviceManager.h> |
| #include <WebCore/RunLoopObserver.h> |
| #include <WebCore/SystemBattery.h> |
| #include <wtf/MachSendRight.h> |
| #include <wtf/cocoa/Entitlements.h> |
| #include <wtf/cocoa/RuntimeApplicationChecksCocoa.h> |
| #endif |
| |
| #if PLATFORM(MAC) |
| #include "DisplayLink.h" |
| #include <WebCore/ImageUtilities.h> |
| #include <WebCore/UTIUtilities.h> |
| #endif |
| |
| #if HAVE(TOUCH_BAR) |
| #include "TouchBarMenuData.h" |
| #include "TouchBarMenuItemData.h" |
| #endif |
| |
| #if PLATFORM(COCOA) || PLATFORM(GTK) |
| #include "ViewSnapshotStore.h" |
| #endif |
| |
| #if PLATFORM(GTK) |
| #include "GtkSettingsManager.h" |
| #include <WebCore/SelectionData.h> |
| #endif |
| |
| #if USE(CAIRO) |
| #include <WebCore/CairoUtilities.h> |
| #endif |
| |
| #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS_FAMILY) |
| #include <WebCore/MediaPlaybackTarget.h> |
| #include <WebCore/WebMediaSessionManager.h> |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) |
| #include "PlaybackSessionManagerProxy.h" |
| #endif |
| |
| #if ENABLE(WEB_AUTHN) |
| #include "WebAuthenticatorCoordinatorProxy.h" |
| #endif |
| |
| #if ENABLE(REMOTE_INSPECTOR) |
| #include <JavaScriptCore/RemoteInspector.h> |
| #endif |
| |
| #if HAVE(SEC_KEY_PROXY) |
| #include "SecKeyProxyStore.h" |
| #endif |
| |
| #if HAVE(APP_SSO) |
| #include "SOAuthorizationCoordinator.h" |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) && ENABLE(DEVICE_ORIENTATION) |
| #include "WebDeviceOrientationUpdateProviderProxy.h" |
| #endif |
| |
| #if ENABLE(DATA_DETECTION) |
| #include "DataDetectionResult.h" |
| #endif |
| |
| #if ENABLE(MEDIA_USAGE) |
| #include "MediaUsageManager.h" |
| #endif |
| |
| #if PLATFORM(COCOA) |
| #include "DefaultWebBrowserChecks.h" |
| #endif |
| |
| #if ENABLE(DATE_AND_TIME_INPUT_TYPES) |
| #include "WebDateTimePicker.h" |
| #endif |
| |
| #if ENABLE(MEDIA_SESSION_COORDINATOR) |
| #include "MediaSessionCoordinatorProxyPrivate.h" |
| #include "RemoteMediaSessionCoordinatorProxy.h" |
| #endif |
| |
| #if HAVE(GROUP_ACTIVITIES) |
| #include "GroupActivitiesSessionNotifier.h" |
| #endif |
| |
| #if ENABLE(APP_HIGHLIGHTS) |
| #include <WebCore/HighlightVisibility.h> |
| #endif |
| |
| #if HAVE(SCREEN_CAPTURE_KIT) |
| #import "DisplayCaptureSessionManager.h" |
| #endif |
| |
| #if HAVE(SC_CONTENT_SHARING_SESSION) |
| #import <WebCore/ScreenCaptureKitSharingSessionManager.h> |
| #endif |
| |
| #if USE(QUICK_LOOK) |
| #include <WebCore/PreviewConverter.h> |
| #endif |
| |
| #define MESSAGE_CHECK(process, assertion) MESSAGE_CHECK_BASE(assertion, process->connection()) |
| #define MESSAGE_CHECK_URL(process, url) MESSAGE_CHECK_BASE(checkURLReceivedFromCurrentOrPreviousWebProcess(process, url), process->connection()) |
| #define MESSAGE_CHECK_COMPLETION(process, assertion, completion) MESSAGE_CHECK_COMPLETION_BASE(assertion, process->connection(), completion) |
| |
| #define WEBPAGEPROXY_RELEASE_LOG(channel, fmt, ...) RELEASE_LOG(channel, "%p - [pageProxyID=%" PRIu64 ", webPageID=%" PRIu64 ", PID=%i] WebPageProxy::" fmt, this, m_identifier.toUInt64(), m_webPageID.toUInt64(), m_process->processIdentifier(), ##__VA_ARGS__) |
| #define WEBPAGEPROXY_RELEASE_LOG_ERROR(channel, fmt, ...) RELEASE_LOG_ERROR(channel, "%p - [pageProxyID=%" PRIu64 ", webPageID=%" PRIu64 ", PID=%i] WebPageProxy::" fmt, this, m_identifier.toUInt64(), m_webPageID.toUInt64(), m_process->processIdentifier(), ##__VA_ARGS__) |
| |
| // Represents the number of wheel events we can hold in the queue before we start pushing them preemptively. |
| static const unsigned wheelEventQueueSizeThreshold = 10; |
| |
| static const Seconds resetRecentCrashCountDelay = 30_s; |
| static unsigned maximumWebProcessRelaunchAttempts = 1; |
| static const Seconds audibleActivityClearDelay = 10_s; |
| static const Seconds tryCloseTimeoutDelay = 50_ms; |
| |
| namespace WebKit { |
| using namespace WebCore; |
| |
| DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, webPageProxyCounter, ("WebPageProxy")); |
| |
| class StorageRequests { |
| WTF_MAKE_NONCOPYABLE(StorageRequests); WTF_MAKE_FAST_ALLOCATED; |
| friend NeverDestroyed<StorageRequests>; |
| public: |
| static StorageRequests& singleton(); |
| |
| void processOrAppend(CompletionHandler<void()>&& completionHandler) |
| { |
| if (m_requestsAreBeingProcessed) { |
| m_requests.append(WTFMove(completionHandler)); |
| return; |
| } |
| m_requestsAreBeingProcessed = true; |
| completionHandler(); |
| } |
| |
| void processNextIfAny() |
| { |
| if (m_requests.isEmpty()) { |
| m_requestsAreBeingProcessed = false; |
| return; |
| } |
| m_requests.takeFirst()(); |
| } |
| |
| private: |
| StorageRequests() { } |
| ~StorageRequests() { } |
| |
| Deque<CompletionHandler<void()>> m_requests; |
| bool m_requestsAreBeingProcessed { false }; |
| }; |
| |
| StorageRequests& StorageRequests::singleton() |
| { |
| static NeverDestroyed<StorageRequests> requests; |
| return requests; |
| } |
| |
| #if !LOG_DISABLED |
| static const char* webMouseEventTypeString(WebEvent::Type type) |
| { |
| switch (type) { |
| case WebEvent::MouseDown: |
| return "MouseDown"; |
| case WebEvent::MouseUp: |
| return "MouseUp"; |
| case WebEvent::MouseMove: |
| return "MouseMove"; |
| case WebEvent::MouseForceChanged: |
| return "MouseForceChanged"; |
| case WebEvent::MouseForceDown: |
| return "MouseForceDown"; |
| case WebEvent::MouseForceUp: |
| return "MouseForceUp"; |
| default: |
| ASSERT_NOT_REACHED(); |
| return "<unknown>"; |
| } |
| } |
| |
| static const char* webKeyboardEventTypeString(WebEvent::Type type) |
| { |
| switch (type) { |
| case WebEvent::KeyDown: |
| return "KeyDown"; |
| case WebEvent::KeyUp: |
| return "KeyUp"; |
| case WebEvent::RawKeyDown: |
| return "RawKeyDown"; |
| case WebEvent::Char: |
| return "Char"; |
| default: |
| ASSERT_NOT_REACHED(); |
| return "<unknown>"; |
| } |
| } |
| #endif // !LOG_DISABLED |
| |
| class PageClientProtector { |
| WTF_MAKE_NONCOPYABLE(PageClientProtector); |
| public: |
| PageClientProtector(PageClient& pageClient) |
| : m_pageClient(pageClient) |
| { |
| m_pageClient->refView(); |
| } |
| |
| ~PageClientProtector() |
| { |
| ASSERT(m_pageClient); |
| m_pageClient->derefView(); |
| } |
| |
| private: |
| WeakPtr<PageClient> m_pageClient; |
| }; |
| |
| void WebPageProxy::forMostVisibleWebPageIfAny(PAL::SessionID sessionID, const SecurityOriginData& origin, CompletionHandler<void(WebPageProxy*)>&& completionHandler) |
| { |
| // FIXME: If not finding right away a visible page, we might want to try again for a given period of time when there is a change of visibility. |
| WebPageProxy* selectedPage = nullptr; |
| WebProcessProxy::forWebPagesWithOrigin(sessionID, origin, [&](auto& page) { |
| if (!page.mainFrame()) |
| return; |
| if (page.isViewVisible() && (!selectedPage || !selectedPage->isViewVisible())) { |
| selectedPage = &page; |
| return; |
| } |
| if (page.isViewFocused() && (!selectedPage || !selectedPage->isViewFocused())) { |
| selectedPage = &page; |
| return; |
| } |
| }); |
| completionHandler(selectedPage); |
| } |
| |
| Ref<WebPageProxy> WebPageProxy::create(PageClient& pageClient, WebProcessProxy& process, Ref<API::PageConfiguration>&& configuration) |
| { |
| return adoptRef(*new WebPageProxy(pageClient, process, WTFMove(configuration))); |
| } |
| |
| WebPageProxy::WebPageProxy(PageClient& pageClient, WebProcessProxy& process, Ref<API::PageConfiguration>&& configuration) |
| : m_identifier(Identifier::generate()) |
| , m_webPageID(PageIdentifier::generate()) |
| , m_pageClient(pageClient) |
| , m_configuration(WTFMove(configuration)) |
| , m_navigationClient(makeUniqueRef<API::NavigationClient>()) |
| , m_historyClient(makeUniqueRef<API::HistoryClient>()) |
| , m_iconLoadingClient(makeUnique<API::IconLoadingClient>()) |
| , m_formClient(makeUnique<API::FormClient>()) |
| , m_uiClient(makeUnique<API::UIClient>()) |
| , m_findClient(makeUnique<API::FindClient>()) |
| , m_findMatchesClient(makeUnique<API::FindMatchesClient>()) |
| #if ENABLE(CONTEXT_MENUS) |
| , m_contextMenuClient(makeUnique<API::ContextMenuClient>()) |
| #endif |
| , m_navigationState(makeUnique<WebNavigationState>()) |
| , m_process(process) |
| , m_pageGroup(*m_configuration->pageGroup()) |
| , m_preferences(*m_configuration->preferences()) |
| , m_userContentController(*m_configuration->userContentController()) |
| , m_visitedLinkStore(*m_configuration->visitedLinkStore()) |
| , m_websiteDataStore(*m_configuration->websiteDataStore()) |
| , m_userAgent(standardUserAgent()) |
| , m_overrideContentSecurityPolicy { m_configuration->overrideContentSecurityPolicy() } |
| #if ENABLE(FULLSCREEN_API) |
| , m_fullscreenClient(makeUnique<API::FullscreenClient>()) |
| #endif |
| , m_geolocationPermissionRequestManager(*this) |
| #if PLATFORM(IOS_FAMILY) |
| , m_audibleActivityTimer(RunLoop::main(), this, &WebPageProxy::clearAudibleActivity) |
| #endif |
| , m_initialCapitalizationEnabled(m_configuration->initialCapitalizationEnabled()) |
| , m_cpuLimit(m_configuration->cpuLimit()) |
| , m_backForwardList(WebBackForwardList::create(*this)) |
| , m_waitsForPaintAfterViewDidMoveToWindow(m_configuration->waitsForPaintAfterViewDidMoveToWindow()) |
| , m_hasRunningProcess(process.state() != WebProcessProxy::State::Terminated) |
| #if HAVE(CVDISPLAYLINK) |
| , m_wheelEventActivityHysteresis([this](PAL::HysteresisState state) { wheelEventHysteresisUpdated(state); }) |
| #endif |
| , m_controlledByAutomation(m_configuration->isControlledByAutomation()) |
| #if PLATFORM(COCOA) |
| , m_isSmartInsertDeleteEnabled(TextChecker::isSmartInsertDeleteEnabled()) |
| #endif |
| , m_pageLoadState(*this) |
| , m_updateReportedMediaCaptureStateTimer(RunLoop::main(), this, &WebPageProxy::updateReportedMediaCaptureState) |
| , m_inspectorController(makeUnique<WebPageInspectorController>(*this)) |
| #if ENABLE(REMOTE_INSPECTOR) |
| , m_inspectorDebuggable(makeUnique<WebPageDebuggable>(*this)) |
| #endif |
| , m_resetRecentCrashCountTimer(RunLoop::main(), this, &WebPageProxy::resetRecentCrashCount) |
| , m_tryCloseTimeoutTimer(RunLoop::main(), this, &WebPageProxy::tryCloseTimedOut) |
| , m_corsDisablingPatterns(m_configuration->corsDisablingPatterns()) |
| #if ENABLE(APP_BOUND_DOMAINS) |
| , m_ignoresAppBoundDomains(m_configuration->ignoresAppBoundDomains()) |
| , m_limitsNavigationsToAppBoundDomains(m_configuration->limitsNavigationsToAppBoundDomains()) |
| #endif |
| , m_notificationManagerMessageHandler(*this) |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| , m_fullscreenVideoTextRecognitionTimer(RunLoop::main(), this, &WebPageProxy::fullscreenVideoTextRecognitionTimerFired) |
| #endif |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "constructor:"); |
| |
| if (!m_configuration->drawsBackground()) |
| m_backgroundColor = Color(Color::transparentBlack); |
| |
| updateActivityState(); |
| updateThrottleState(); |
| updateHiddenPageThrottlingAutoIncreases(); |
| |
| #if HAVE(OUT_OF_PROCESS_LAYER_HOSTING) |
| m_layerHostingMode = m_activityState & ActivityState::IsInWindow ? WebPageProxy::pageClient().viewLayerHostingMode() : LayerHostingMode::OutOfProcess; |
| #endif |
| |
| platformInitialize(); |
| |
| #ifndef NDEBUG |
| webPageProxyCounter.increment(); |
| #endif |
| |
| WebProcessPool::statistics().wkPageCount++; |
| |
| m_preferences->addPage(*this); |
| m_pageGroup->addPage(*this); |
| |
| m_inspector = WebInspectorUIProxy::create(*this); |
| |
| if (hasRunningProcess()) |
| didAttachToRunningProcess(); |
| |
| addAllMessageReceivers(); |
| |
| #if PLATFORM(IOS_FAMILY) |
| DeprecatedGlobalSettings::setDisableScreenSizeOverride(m_preferences->disableScreenSizeOverride()); |
| |
| if (m_configuration->preferences()->serviceWorkerEntitlementDisabledForTesting()) |
| disableServiceWorkerEntitlementInNetworkProcess(); |
| #endif |
| |
| #if PLATFORM(COCOA) |
| m_activityStateChangeDispatcher = makeUnique<RunLoopObserver>(static_cast<CFIndex>(RunLoopObserver::WellKnownRunLoopOrders::ActivityStateChange), [this] { |
| this->dispatchActivityStateChange(); |
| }); |
| #endif |
| |
| #if ENABLE(REMOTE_INSPECTOR) |
| m_inspectorDebuggable->setRemoteDebuggingAllowed(true); |
| m_inspectorDebuggable->init(); |
| #endif |
| m_inspectorController->init(); |
| |
| #if ENABLE(IPC_TESTING_API) |
| if (m_preferences->ipcTestingAPIEnabled()) |
| process.setIgnoreInvalidMessageForTesting(); |
| #endif |
| |
| #if ENABLE(MEDIA_SESSION_COORDINATOR) && HAVE(GROUP_ACTIVITIES) |
| if (m_preferences->mediaSessionCoordinatorEnabled()) |
| GroupActivitiesSessionNotifier::sharedNotifier().addWebPage(*this); |
| #endif |
| } |
| |
| WebPageProxy::~WebPageProxy() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "destructor:"); |
| |
| ASSERT(m_process->webPage(m_identifier) != this); |
| #if ASSERT_ENABLED |
| for (WebPageProxy* page : m_process->pages()) |
| ASSERT(page != this); |
| #endif |
| |
| setPageLoadStateObserver(nullptr); |
| |
| if (!m_isClosed) |
| close(); |
| |
| WebProcessPool::statistics().wkPageCount--; |
| |
| if (m_spellDocumentTag) |
| TextChecker::closeSpellDocumentWithTag(m_spellDocumentTag.value()); |
| |
| m_preferences->removePage(*this); |
| m_pageGroup->removePage(*this); |
| |
| #ifndef NDEBUG |
| webPageProxyCounter.decrement(); |
| #endif |
| |
| #if PLATFORM(MACCATALYST) |
| EndowmentStateTracker::singleton().removeClient(*this); |
| #endif |
| |
| for (auto& callback : m_nextActivityStateChangeCallbacks) |
| callback(); |
| |
| if (auto* networkProcess = websiteDataStore().networkProcessIfExists()) |
| networkProcess->send(Messages::NetworkProcess::RemoveWebPageNetworkParameters(sessionID(), m_identifier), 0); |
| |
| #if ENABLE(MEDIA_SESSION_COORDINATOR) && HAVE(GROUP_ACTIVITIES) |
| if (m_preferences->mediaSessionCoordinatorEnabled()) |
| GroupActivitiesSessionNotifier::sharedNotifier().removeWebPage(*this); |
| #endif |
| } |
| |
| void WebPageProxy::addAllMessageReceivers() |
| { |
| m_process->addMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_webPageID, *this); |
| m_process->addMessageReceiver(Messages::NotificationManagerMessageHandler::messageReceiverName(), m_webPageID, m_notificationManagerMessageHandler); |
| } |
| |
| void WebPageProxy::removeAllMessageReceivers() |
| { |
| m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_webPageID); |
| m_process->removeMessageReceiver(Messages::NotificationManagerMessageHandler::messageReceiverName(), m_webPageID); |
| } |
| |
| // FIXME: Should return a const PageClient& and add a separate non-const |
| // version of this function, but several PageClient methods will need to become |
| // const for this to be possible. |
| PageClient& WebPageProxy::pageClient() const |
| { |
| ASSERT(m_pageClient); |
| return *m_pageClient; |
| } |
| |
| PAL::SessionID WebPageProxy::sessionID() const |
| { |
| return m_websiteDataStore->sessionID(); |
| } |
| |
| DrawingAreaProxy* WebPageProxy::provisionalDrawingArea() const |
| { |
| if (m_provisionalPage && m_provisionalPage->drawingArea()) |
| return m_provisionalPage->drawingArea(); |
| return drawingArea(); |
| } |
| |
| const API::PageConfiguration& WebPageProxy::configuration() const |
| { |
| return m_configuration.get(); |
| } |
| |
| ProcessID WebPageProxy::gpuProcessIdentifier() const |
| { |
| if (m_isClosed) |
| return 0; |
| |
| #if ENABLE(GPU_PROCESS) |
| if (auto* gpuProcess = process().processPool().gpuProcess()) |
| return gpuProcess->processIdentifier(); |
| #endif |
| |
| return 0; |
| } |
| |
| ProcessID WebPageProxy::processIdentifier() const |
| { |
| if (m_isClosed) |
| return 0; |
| |
| return m_process->processIdentifier(); |
| } |
| |
| bool WebPageProxy::hasRunningProcess() const |
| { |
| // A page that has been explicitly closed is never valid. |
| if (m_isClosed) |
| return false; |
| |
| return m_hasRunningProcess; |
| } |
| |
| void WebPageProxy::notifyProcessPoolToPrewarm() |
| { |
| m_process->processPool().didReachGoodTimeToPrewarm(); |
| } |
| |
| void WebPageProxy::setPreferences(WebPreferences& preferences) |
| { |
| if (&preferences == m_preferences.ptr()) |
| return; |
| |
| m_preferences->removePage(*this); |
| m_preferences = preferences; |
| m_preferences->addPage(*this); |
| |
| preferencesDidChange(); |
| } |
| |
| void WebPageProxy::setHistoryClient(UniqueRef<API::HistoryClient>&& historyClient) |
| { |
| m_historyClient = WTFMove(historyClient); |
| } |
| |
| void WebPageProxy::setNavigationClient(UniqueRef<API::NavigationClient>&& navigationClient) |
| { |
| m_navigationClient = WTFMove(navigationClient); |
| } |
| |
| void WebPageProxy::setLoaderClient(std::unique_ptr<API::LoaderClient>&& loaderClient) |
| { |
| m_loaderClient = WTFMove(loaderClient); |
| } |
| |
| void WebPageProxy::setPolicyClient(std::unique_ptr<API::PolicyClient>&& policyClient) |
| { |
| m_policyClient = WTFMove(policyClient); |
| } |
| |
| void WebPageProxy::setFormClient(std::unique_ptr<API::FormClient>&& formClient) |
| { |
| if (!formClient) { |
| m_formClient = makeUnique<API::FormClient>(); |
| return; |
| } |
| |
| m_formClient = WTFMove(formClient); |
| } |
| |
| void WebPageProxy::setUIClient(std::unique_ptr<API::UIClient>&& uiClient) |
| { |
| if (!uiClient) { |
| m_uiClient = makeUnique<API::UIClient>(); |
| return; |
| } |
| |
| m_uiClient = WTFMove(uiClient); |
| |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetCanRunBeforeUnloadConfirmPanel(m_uiClient->canRunBeforeUnloadConfirmPanel())); |
| |
| setCanRunModal(m_uiClient->canRunModal()); |
| setNeedsFontAttributes(m_uiClient->needsFontAttributes()); |
| } |
| |
| void WebPageProxy::setIconLoadingClient(std::unique_ptr<API::IconLoadingClient>&& iconLoadingClient) |
| { |
| bool hasClient = iconLoadingClient.get(); |
| if (!iconLoadingClient) |
| m_iconLoadingClient = makeUnique<API::IconLoadingClient>(); |
| else |
| m_iconLoadingClient = WTFMove(iconLoadingClient); |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetUseIconLoadingClient(hasClient)); |
| } |
| |
| void WebPageProxy::setPageLoadStateObserver(std::unique_ptr<PageLoadState::Observer>&& observer) |
| { |
| if (m_pageLoadStateObserver) |
| pageLoadState().removeObserver(*m_pageLoadStateObserver); |
| m_pageLoadStateObserver = WTFMove(observer); |
| if (m_pageLoadStateObserver) |
| pageLoadState().addObserver(*m_pageLoadStateObserver); |
| } |
| |
| void WebPageProxy::setFindClient(std::unique_ptr<API::FindClient>&& findClient) |
| { |
| if (!findClient) { |
| m_findClient = makeUnique<API::FindClient>(); |
| return; |
| } |
| |
| m_findClient = WTFMove(findClient); |
| } |
| |
| void WebPageProxy::setFindMatchesClient(std::unique_ptr<API::FindMatchesClient>&& findMatchesClient) |
| { |
| if (!findMatchesClient) { |
| m_findMatchesClient = makeUnique<API::FindMatchesClient>(); |
| return; |
| } |
| |
| m_findMatchesClient = WTFMove(findMatchesClient); |
| } |
| |
| void WebPageProxy::setDiagnosticLoggingClient(std::unique_ptr<API::DiagnosticLoggingClient>&& diagnosticLoggingClient) |
| { |
| m_diagnosticLoggingClient = WTFMove(diagnosticLoggingClient); |
| } |
| |
| #if ENABLE(CONTEXT_MENUS) |
| void WebPageProxy::setContextMenuClient(std::unique_ptr<API::ContextMenuClient>&& contextMenuClient) |
| { |
| if (!contextMenuClient) { |
| m_contextMenuClient = makeUnique<API::ContextMenuClient>(); |
| return; |
| } |
| |
| m_contextMenuClient = WTFMove(contextMenuClient); |
| } |
| #endif |
| |
| void WebPageProxy::setInjectedBundleClient(const WKPageInjectedBundleClientBase* client) |
| { |
| if (!client) { |
| m_injectedBundleClient = nullptr; |
| return; |
| } |
| |
| m_injectedBundleClient = makeUnique<WebPageInjectedBundleClient>(); |
| m_injectedBundleClient->initialize(client); |
| } |
| |
| void WebPageProxy::setResourceLoadClient(std::unique_ptr<API::ResourceLoadClient>&& client) |
| { |
| bool hadResourceLoadClient = !!m_resourceLoadClient; |
| m_resourceLoadClient = WTFMove(client); |
| bool hasResourceLoadClient = !!m_resourceLoadClient; |
| if (hadResourceLoadClient != hasResourceLoadClient) |
| send(Messages::WebPage::SetHasResourceLoadClient(hasResourceLoadClient)); |
| } |
| |
| void WebPageProxy::handleMessage(IPC::Connection& connection, const String& messageName, const WebKit::UserData& messageBody) |
| { |
| ASSERT(m_process->connection() == &connection); |
| |
| if (!m_injectedBundleClient) |
| return; |
| |
| m_injectedBundleClient->didReceiveMessageFromInjectedBundle(this, messageName, m_process->transformHandlesToObjects(messageBody.object()).get()); |
| } |
| |
| void WebPageProxy::handleSynchronousMessage(IPC::Connection& connection, const String& messageName, const UserData& messageBody, CompletionHandler<void(UserData&&)>&& completionHandler) |
| { |
| ASSERT(m_process->connection() == &connection); |
| |
| if (!m_injectedBundleClient) |
| return completionHandler({ }); |
| |
| RefPtr<API::Object> returnData; |
| m_injectedBundleClient->didReceiveSynchronousMessageFromInjectedBundle(this, messageName, m_process->transformHandlesToObjects(messageBody.object()).get(), [completionHandler = WTFMove(completionHandler), process = m_process] (RefPtr<API::Object>&& returnData) mutable { |
| completionHandler(UserData(process->transformObjectsToHandles(returnData.get()))); |
| }); |
| } |
| |
| void WebPageProxy::launchProcess(const RegistrableDomain& registrableDomain, ProcessLaunchReason reason) |
| { |
| ASSERT(!m_isClosed); |
| ASSERT(!hasRunningProcess()); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "launchProcess:"); |
| |
| // In case we are currently connected to the dummy process, we need to make sure the inspector proxy |
| // disconnects from the dummy process first. |
| m_inspector->reset(); |
| |
| m_process->removeWebPage(*this, WebProcessProxy::EndsUsingDataStore::Yes); |
| removeAllMessageReceivers(); |
| |
| auto& processPool = m_process->processPool(); |
| |
| auto* relatedPage = m_configuration->relatedPage(); |
| if (relatedPage && !relatedPage->isClosed() && reason == ProcessLaunchReason::InitialProcess) { |
| m_process = relatedPage->ensureRunningProcess(); |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "launchProcess: Using process (process=%p, PID=%i) from related page", m_process.ptr(), m_process->processIdentifier()); |
| } else |
| m_process = processPool.processForRegistrableDomain(m_websiteDataStore.get(), registrableDomain, shouldEnableCaptivePortalMode() ? WebProcessProxy::CaptivePortalMode::Enabled : WebProcessProxy::CaptivePortalMode::Disabled); |
| |
| m_hasRunningProcess = true; |
| m_shouldReloadDueToCrashWhenVisible = false; |
| m_isCaptivePortalModeExplicitlySet = m_configuration->isCaptivePortalModeExplicitlySet(); |
| |
| m_process->addExistingWebPage(*this, WebProcessProxy::BeginsUsingDataStore::Yes); |
| addAllMessageReceivers(); |
| |
| #if ENABLE(IPC_TESTING_API) |
| if (m_preferences->store().getBoolValueForKey(WebPreferencesKey::ipcTestingAPIEnabledKey())) |
| m_process->setIgnoreInvalidMessageForTesting(); |
| #endif |
| |
| finishAttachingToWebProcess(reason); |
| |
| auto pendingInjectedBundleMessage = WTFMove(m_pendingInjectedBundleMessages); |
| for (auto& message : pendingInjectedBundleMessage) |
| send(Messages::WebPage::PostInjectedBundleMessage(message.messageName, UserData(process().transformObjectsToHandles(message.messageBody.get()).get()))); |
| } |
| |
| bool WebPageProxy::suspendCurrentPageIfPossible(API::Navigation& navigation, std::optional<FrameIdentifier> mainFrameID, ProcessSwapRequestedByClient processSwapRequestedByClient, ShouldDelayClosingUntilFirstLayerFlush shouldDelayClosingUntilFirstLayerFlush) |
| { |
| m_suspendedPageKeptToPreventFlashing = nullptr; |
| m_lastSuspendedPage = nullptr; |
| |
| if (!mainFrameID) |
| return false; |
| |
| if (!hasCommittedAnyProvisionalLoads()) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i because has not committed any load yet", m_process->processIdentifier()); |
| return false; |
| } |
| |
| if (isPageOpenedByDOMShowingInitialEmptyDocument()) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i because it is showing the initial empty document", m_process->processIdentifier()); |
| return false; |
| } |
| |
| auto* fromItem = navigation.fromItem(); |
| |
| // If the source and the destination back / forward list items are the same, then this is a client-side redirect. In this case, |
| // there is no need to suspend the previous page as there will be no way to get back to it. |
| if (fromItem && fromItem == m_backForwardList->currentItem()) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i because this is a client-side redirect", m_process->processIdentifier()); |
| return false; |
| } |
| |
| if (fromItem && fromItem->url() != pageLoadState().url()) { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i because fromItem's URL does not match the page URL.", m_process->processIdentifier()); |
| return false; |
| } |
| |
| bool needsSuspendedPageToPreventFlashing = shouldDelayClosingUntilFirstLayerFlush == ShouldDelayClosingUntilFirstLayerFlush::Yes; |
| if (!needsSuspendedPageToPreventFlashing && (!fromItem || !shouldUseBackForwardCache())) { |
| if (!fromItem) |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i there is no associated WebBackForwardListItem", m_process->processIdentifier()); |
| else |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i the back / forward cache is disabled", m_process->processIdentifier()); |
| return false; |
| } |
| |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "suspendCurrentPageIfPossible: Suspending current page for process pid %i", m_process->processIdentifier()); |
| auto suspendedPage = makeUnique<SuspendedPageProxy>(*this, m_process.copyRef(), *mainFrameID, shouldDelayClosingUntilFirstLayerFlush); |
| |
| LOG(ProcessSwapping, "WebPageProxy %" PRIu64 " created suspended page %s for process pid %i, back/forward item %s" PRIu64, identifier().toUInt64(), suspendedPage->loggingString(), m_process->processIdentifier(), fromItem ? fromItem->itemID().logString() : 0); |
| |
| m_lastSuspendedPage = *suspendedPage; |
| |
| if (fromItem && shouldUseBackForwardCache()) |
| backForwardCache().addEntry(*fromItem, WTFMove(suspendedPage)); |
| else { |
| ASSERT(needsSuspendedPageToPreventFlashing); |
| m_suspendedPageKeptToPreventFlashing = WTFMove(suspendedPage); |
| } |
| |
| return true; |
| } |
| |
| WebBackForwardCache& WebPageProxy::backForwardCache() const |
| { |
| return process().processPool().backForwardCache(); |
| } |
| |
| bool WebPageProxy::shouldUseBackForwardCache() const |
| { |
| return m_preferences->usesBackForwardCache() && backForwardCache().capacity() > 0; |
| } |
| |
| void WebPageProxy::swapToProvisionalPage(std::unique_ptr<ProvisionalPageProxy> provisionalPage) |
| { |
| ASSERT(!m_isClosed); |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "swapToProvisionalPage: newWebPageID=%" PRIu64, provisionalPage->webPageID().toUInt64()); |
| |
| m_process = provisionalPage->process(); |
| m_webPageID = provisionalPage->webPageID(); |
| pageClient().didChangeWebPageID(); |
| ASSERT(m_process->websiteDataStore()); |
| m_websiteDataStore = *m_process->websiteDataStore(); |
| |
| #if HAVE(VISIBILITY_PROPAGATION_VIEW) |
| m_contextIDForVisibilityPropagationInWebProcess = provisionalPage->contextIDForVisibilityPropagationInWebProcess(); |
| #if ENABLE(GPU_PROCESS) |
| m_contextIDForVisibilityPropagationInGPUProcess = provisionalPage->contextIDForVisibilityPropagationInGPUProcess(); |
| #endif |
| #endif |
| |
| // FIXME: Do we really need to disable this logging in ephemeral sessions? |
| if (m_logger) |
| m_logger->setEnabled(this, !sessionID().isEphemeral()); |
| |
| m_hasRunningProcess = true; |
| |
| ASSERT(!m_drawingArea); |
| setDrawingArea(provisionalPage->takeDrawingArea()); |
| ASSERT(!m_mainFrame); |
| m_mainFrame = provisionalPage->mainFrame(); |
| |
| m_process->addExistingWebPage(*this, WebProcessProxy::BeginsUsingDataStore::No); |
| addAllMessageReceivers(); |
| |
| finishAttachingToWebProcess(ProcessLaunchReason::ProcessSwap); |
| |
| #if PLATFORM(IOS_FAMILY) |
| // On iOS, the displayID is derived from the webPageID. |
| m_displayID = generateDisplayIDFromPageID(); |
| // FIXME: We may want to send WindowScreenDidChange on non-iOS platforms too. |
| send(Messages::WebPage::WindowScreenDidChange(*m_displayID, std::nullopt)); |
| #endif |
| |
| #if PLATFORM(COCOA) |
| auto accessibilityToken = provisionalPage->takeAccessibilityToken(); |
| if (!accessibilityToken.isEmpty()) |
| registerWebProcessAccessibilityToken({ accessibilityToken.data(), accessibilityToken.size() }); |
| #endif |
| #if PLATFORM(GTK) || PLATFORM(WPE) |
| auto accessibilityPlugID = provisionalPage->accessibilityPlugID(); |
| if (!accessibilityPlugID.isEmpty()) |
| bindAccessibilityTree(accessibilityPlugID); |
| #endif |
| } |
| |
| void WebPageProxy::finishAttachingToWebProcess(ProcessLaunchReason reason) |
| { |
| ASSERT(m_process->state() != AuxiliaryProcessProxy::State::Terminated); |
| |
| updateActivityState(); |
| updateThrottleState(); |
| |
| didAttachToRunningProcess(); |
| |
| // In the process-swap case, the ProvisionalPageProxy already took care of initializing the WebPage in the WebProcess. |
| if (reason != ProcessLaunchReason::ProcessSwap) |
| initializeWebPage(); |
| |
| m_inspector->updateForNewPageProcess(*this); |
| |
| #if ENABLE(REMOTE_INSPECTOR) |
| remoteInspectorInformationDidChange(); |
| #endif |
| |
| updateWheelEventActivityAfterProcessSwap(); |
| |
| pageClient().didRelaunchProcess(); |
| m_pageLoadState.didSwapWebProcesses(); |
| if (reason != ProcessLaunchReason::InitialProcess) |
| m_drawingArea->waitForBackingStoreUpdateOnNextPaint(); |
| } |
| |
| void WebPageProxy::didAttachToRunningProcess() |
| { |
| ASSERT(hasRunningProcess()); |
| |
| #if ENABLE(FULLSCREEN_API) |
| ASSERT(!m_fullScreenManager); |
| m_fullScreenManager = makeUnique<WebFullScreenManagerProxy>(*this, pageClient().fullScreenManagerProxyClient()); |
| #endif |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| ASSERT(!m_playbackSessionManager); |
| m_playbackSessionManager = PlaybackSessionManagerProxy::create(*this); |
| ASSERT(!m_videoFullscreenManager); |
| m_videoFullscreenManager = VideoFullscreenManagerProxy::create(*this, *m_playbackSessionManager); |
| if (m_videoFullscreenManager) |
| m_videoFullscreenManager->setMockVideoPresentationModeEnabled(m_mockVideoPresentationModeEnabled); |
| #endif |
| |
| #if ENABLE(APPLE_PAY) |
| ASSERT(!m_paymentCoordinator); |
| m_paymentCoordinator = makeUnique<WebPaymentCoordinatorProxy>(*this); |
| #endif |
| |
| #if USE(SYSTEM_PREVIEW) |
| ASSERT(!m_systemPreviewController); |
| m_systemPreviewController = makeUnique<SystemPreviewController>(*this); |
| #endif |
| |
| #if ENABLE(ARKIT_INLINE_PREVIEW) |
| if (m_preferences->modelElementEnabled()) { |
| ASSERT(!m_modelElementController); |
| m_modelElementController = makeUnique<ModelElementController>(*this); |
| } |
| #endif |
| |
| #if ENABLE(WEB_AUTHN) |
| ASSERT(!m_credentialsMessenger); |
| m_credentialsMessenger = makeUnique<WebAuthenticatorCoordinatorProxy>(*this); |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) && ENABLE(DEVICE_ORIENTATION) |
| ASSERT(!m_webDeviceOrientationUpdateProviderProxy); |
| m_webDeviceOrientationUpdateProviderProxy = makeUnique<WebDeviceOrientationUpdateProviderProxy>(*this); |
| #endif |
| |
| #if ENABLE(WEBXR) && !USE(OPENXR) |
| ASSERT(!m_xrSystem); |
| m_xrSystem = makeUnique<PlatformXRSystem>(*this); |
| #endif |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::launchProcessForReload() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "launchProcessForReload:"); |
| |
| if (m_isClosed) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "launchProcessForReload: page is closed"); |
| return nullptr; |
| } |
| |
| ASSERT(!hasRunningProcess()); |
| auto registrableDomain = m_backForwardList->currentItem() ? RegistrableDomain { URL { m_backForwardList->currentItem()->url() } } : RegistrableDomain { }; |
| launchProcess(registrableDomain, ProcessLaunchReason::Crash); |
| |
| if (!m_backForwardList->currentItem()) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "launchProcessForReload: no current item to reload"); |
| return nullptr; |
| } |
| |
| auto navigation = m_navigationState->createReloadNavigation(m_backForwardList->currentItem()); |
| |
| String url = currentURL(); |
| if (!url.isEmpty()) { |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setPendingAPIRequest(transaction, { navigation->navigationID(), url }); |
| } |
| |
| // We allow stale content when reloading a WebProcess that's been killed or crashed. |
| send(Messages::WebPage::GoToBackForwardItem(navigation->navigationID(), m_backForwardList->currentItem()->itemID(), FrameLoadType::IndexedBackForward, ShouldTreatAsContinuingLoad::No, std::nullopt, m_lastNavigationWasAppInitiated, std::nullopt)); |
| m_process->startResponsivenessTimer(); |
| |
| return navigation; |
| } |
| |
| void WebPageProxy::setDrawingArea(std::unique_ptr<DrawingAreaProxy>&& drawingArea) |
| { |
| m_drawingArea = WTFMove(drawingArea); |
| if (!m_drawingArea) |
| return; |
| |
| m_drawingArea->startReceivingMessages(); |
| m_drawingArea->setSize(viewSize()); |
| |
| #if ENABLE(ASYNC_SCROLLING) && PLATFORM(COCOA) |
| if (m_drawingArea->type() == DrawingAreaType::RemoteLayerTree) { |
| m_scrollingCoordinatorProxy = makeUnique<RemoteScrollingCoordinatorProxy>(*this); |
| #if PLATFORM(IOS_FAMILY) |
| // On iOS, main frame scrolls are sent in terms of visible rect updates. |
| m_scrollingCoordinatorProxy->setPropagatesMainFrameScrolls(false); |
| #endif |
| } |
| #endif |
| } |
| |
| void WebPageProxy::initializeWebPage() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| setDrawingArea(pageClient().createDrawingAreaProxy(m_process)); |
| ASSERT(m_drawingArea); |
| |
| #if ENABLE(REMOTE_INSPECTOR) |
| // Initialize remote inspector connection now that we have a sub-process that is hosting one of our web views. |
| Inspector::RemoteInspector::singleton(); |
| #endif |
| |
| if (auto& attributedBundleIdentifier = m_configuration->attributedBundleIdentifier(); !!attributedBundleIdentifier) { |
| WebPageNetworkParameters parameters { attributedBundleIdentifier }; |
| websiteDataStore().networkProcess().send(Messages::NetworkProcess::AddWebPageNetworkParameters(sessionID(), m_identifier, WTFMove(parameters)), 0); |
| } |
| |
| send(Messages::WebProcess::CreateWebPage(m_webPageID, creationParameters(m_process, *m_drawingArea)), 0); |
| |
| m_process->addVisitedLinkStoreUser(visitedLinkStore(), m_identifier); |
| } |
| |
| void WebPageProxy::close() |
| { |
| if (m_isClosed) |
| return; |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "close:"); |
| |
| m_isClosed = true; |
| |
| reportPageLoadResult(ResourceError { ResourceError::Type::Cancellation }); |
| |
| if (m_activePopupMenu) |
| m_activePopupMenu->cancelTracking(); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->willClosePage(*this); |
| } |
| |
| #if ENABLE(CONTEXT_MENUS) |
| m_activeContextMenu = nullptr; |
| #endif |
| |
| m_provisionalPage = nullptr; |
| |
| m_inspector->invalidate(); |
| |
| m_backForwardList->pageClosed(); |
| m_inspectorController->pageClosed(); |
| #if ENABLE(REMOTE_INSPECTOR) |
| m_inspectorDebuggable = nullptr; |
| #endif |
| pageClient().pageClosed(); |
| |
| m_process->disconnectFramesFromPage(this); |
| |
| m_loaderClient = nullptr; |
| m_navigationClient = makeUniqueRef<API::NavigationClient>(); |
| m_policyClient = nullptr; |
| m_iconLoadingClient = makeUnique<API::IconLoadingClient>(); |
| m_formClient = makeUnique<API::FormClient>(); |
| m_uiClient = makeUnique<API::UIClient>(); |
| m_findClient = makeUnique<API::FindClient>(); |
| m_findMatchesClient = makeUnique<API::FindMatchesClient>(); |
| m_diagnosticLoggingClient = nullptr; |
| #if ENABLE(CONTEXT_MENUS) |
| m_contextMenuClient = makeUnique<API::ContextMenuClient>(); |
| #endif |
| #if ENABLE(FULLSCREEN_API) |
| m_fullscreenClient = makeUnique<API::FullscreenClient>(); |
| #endif |
| |
| resetState(ResetStateReason::PageInvalidated); |
| |
| m_process->processPool().backForwardCache().removeEntriesForPage(*this); |
| |
| RunLoop::current().dispatch([destinationID = messageSenderDestinationID(), protectedProcess = m_process.copyRef(), preventProcessShutdownScope = m_process->shutdownPreventingScope()] { |
| protectedProcess->send(Messages::WebPage::Close(), destinationID); |
| }); |
| m_process->removeWebPage(*this, WebProcessProxy::EndsUsingDataStore::Yes); |
| removeAllMessageReceivers(); |
| m_process->processPool().supplement<WebNotificationManagerProxy>()->clearNotifications(this); |
| |
| // Null out related WebPageProxy to avoid leaks. |
| m_configuration->setRelatedPage(nullptr); |
| |
| #if PLATFORM(IOS_FAMILY) |
| // Make sure we don't hold a process assertion after getting closed. |
| m_isVisibleActivity = nullptr; |
| m_isAudibleActivity = nullptr; |
| m_isCapturingActivity = nullptr; |
| m_openingAppLinkActivity = nullptr; |
| m_audibleActivityTimer.stop(); |
| #endif |
| |
| stopAllURLSchemeTasks(); |
| updatePlayingMediaDidChange(MediaProducer::IsNotPlaying); |
| } |
| |
| bool WebPageProxy::tryClose() |
| { |
| if (!hasRunningProcess()) |
| return true; |
| |
| WEBPAGEPROXY_RELEASE_LOG(Process, "tryClose:"); |
| |
| // Close without delay if the process allows it. Our goal is to terminate |
| // the process, so we check a per-process status bit. |
| if (m_process->isSuddenTerminationEnabled()) |
| return true; |
| |
| m_tryCloseTimeoutTimer.startOneShot(tryCloseTimeoutDelay); |
| sendWithAsyncReply(Messages::WebPage::TryClose(), [this, weakThis = WeakPtr { *this }](bool shouldClose) { |
| if (!weakThis) |
| return; |
| |
| // If we timed out, don't ask the client to close again. |
| if (!m_tryCloseTimeoutTimer.isActive()) |
| return; |
| |
| m_tryCloseTimeoutTimer.stop(); |
| if (shouldClose) |
| closePage(); |
| }); |
| return false; |
| } |
| |
| void WebPageProxy::tryCloseTimedOut() |
| { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Process, "tryCloseTimedOut: Timed out waiting for the process to respond to the WebPage::TryClose IPC, closing the page now"); |
| closePage(); |
| } |
| |
| void WebPageProxy::maybeInitializeSandboxExtensionHandle(WebProcessProxy& process, const URL& url, const URL& resourceDirectoryURL, SandboxExtension::Handle& sandboxExtensionHandle, bool checkAssumedReadAccessToResourceURL) |
| { |
| if (!url.isLocalFile()) |
| return; |
| |
| #if HAVE(AUDIT_TOKEN) |
| // If the process is still launching then it does not have a PID yet. We will take care of creating the sandbox extension |
| // once the process has finished launching. |
| if (process.isLaunching() || process.wasTerminated()) |
| return; |
| #endif |
| |
| if (!resourceDirectoryURL.isEmpty()) { |
| if (checkAssumedReadAccessToResourceURL && process.hasAssumedReadAccessToURL(resourceDirectoryURL)) |
| return; |
| |
| bool createdExtension = false; |
| #if HAVE(AUDIT_TOKEN) |
| ASSERT(process.connection() && process.connection()->getAuditToken()); |
| if (process.connection() && process.connection()->getAuditToken()) { |
| if (auto handle = SandboxExtension::createHandleForReadByAuditToken(resourceDirectoryURL.fileSystemPath(), *(process.connection()->getAuditToken()))) { |
| sandboxExtensionHandle = WTFMove(*handle); |
| createdExtension = true; |
| } |
| } else |
| #endif |
| { |
| if (auto handle = SandboxExtension::createHandle(resourceDirectoryURL.fileSystemPath(), SandboxExtension::Type::ReadOnly)) { |
| sandboxExtensionHandle = WTFMove(*handle); |
| createdExtension = true; |
| } |
| } |
| |
| if (createdExtension) { |
| process.assumeReadAccessToBaseURL(*this, resourceDirectoryURL.string()); |
| return; |
| } |
| } |
| |
| if (process.hasAssumedReadAccessToURL(url)) |
| return; |
| |
| // Inspector resources are in a directory with assumed access. |
| RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!WebKit::isInspectorPage(*this)); |
| |
| bool createdExtension = false; |
| #if HAVE(AUDIT_TOKEN) |
| ASSERT(process.connection() && process.connection()->getAuditToken()); |
| if (process.connection() && process.connection()->getAuditToken()) { |
| if (auto handle = SandboxExtension::createHandleForReadByAuditToken("/"_s, *(process.connection()->getAuditToken()))) { |
| createdExtension = true; |
| sandboxExtensionHandle = WTFMove(*handle); |
| } |
| } else |
| #endif |
| { |
| if (auto handle = SandboxExtension::createHandle("/"_s, SandboxExtension::Type::ReadOnly)) { |
| createdExtension = true; |
| sandboxExtensionHandle = WTFMove(*handle); |
| } |
| } |
| |
| if (createdExtension) { |
| willAcquireUniversalFileReadSandboxExtension(process); |
| return; |
| } |
| |
| #if PLATFORM(COCOA) |
| if (!linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::NoUnconditionalUniversalSandboxExtension)) |
| willAcquireUniversalFileReadSandboxExtension(process); |
| #endif |
| |
| // We failed to issue an universal file read access sandbox, fall back to issuing one for the base URL instead. |
| auto baseURL = url.truncatedForUseAsBase(); |
| auto basePath = baseURL.fileSystemPath(); |
| if (basePath.isNull()) |
| return; |
| #if HAVE(AUDIT_TOKEN) |
| if (process.connection() && process.connection()->getAuditToken()) { |
| if (auto handle = SandboxExtension::createHandleForReadByAuditToken(basePath, *(process.connection()->getAuditToken()))) { |
| sandboxExtensionHandle = WTFMove(*handle); |
| createdExtension = true; |
| } |
| } else |
| #endif |
| { |
| if (auto handle = SandboxExtension::createHandle(basePath, SandboxExtension::Type::ReadOnly)) { |
| sandboxExtensionHandle = WTFMove(*handle); |
| createdExtension = true; |
| } |
| } |
| |
| if (createdExtension) |
| process.assumeReadAccessToBaseURL(*this, baseURL.string()); |
| } |
| |
| #if !PLATFORM(COCOA) |
| |
| void WebPageProxy::addPlatformLoadParameters(WebProcessProxy&, LoadParameters&) |
| { |
| } |
| |
| #endif |
| |
| WebProcessProxy& WebPageProxy::ensureRunningProcess() |
| { |
| if (!hasRunningProcess()) |
| launchProcess({ }, ProcessLaunchReason::InitialProcess); |
| |
| return m_process; |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::loadRequest(ResourceRequest&& request, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData) |
| { |
| if (m_isClosed) |
| return nullptr; |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadRequest:"); |
| |
| if (!hasRunningProcess()) |
| launchProcess(RegistrableDomain { request.url() }, ProcessLaunchReason::InitialProcess); |
| |
| auto navigation = m_navigationState->createLoadRequestNavigation(ResourceRequest(request), m_backForwardList->currentItem()); |
| |
| if (shouldForceForegroundPriorityForClientNavigation()) |
| navigation->setClientNavigationActivity(process().throttler().foregroundActivity("Client navigation"_s)); |
| |
| #if PLATFORM(COCOA) |
| setLastNavigationWasAppInitiated(request); |
| #endif |
| |
| loadRequestWithNavigationShared(m_process.copyRef(), m_webPageID, navigation.get(), WTFMove(request), shouldOpenExternalURLsPolicy, userData, ShouldTreatAsContinuingLoad::No, isNavigatingToAppBoundDomain()); |
| return navigation; |
| } |
| |
| void WebPageProxy::loadRequestWithNavigationShared(Ref<WebProcessProxy>&& process, WebCore::PageIdentifier webPageID, API::Navigation& navigation, ResourceRequest&& request, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData, ShouldTreatAsContinuingLoad shouldTreatAsContinuingLoad, std::optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain, std::optional<WebsitePoliciesData>&& websitePolicies, std::optional<NetworkResourceLoadIdentifier> existingNetworkResourceLoadIdentifierToResume) |
| { |
| ASSERT(!m_isClosed); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadRequestWithNavigationShared:"); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| auto url = request.url(); |
| if (shouldTreatAsContinuingLoad == ShouldTreatAsContinuingLoad::No) |
| m_pageLoadState.setPendingAPIRequest(transaction, { navigation.navigationID(), url.string() }); |
| |
| LoadParameters loadParameters; |
| loadParameters.navigationID = navigation.navigationID(); |
| loadParameters.request = WTFMove(request); |
| loadParameters.shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicy; |
| loadParameters.userData = UserData(process->transformObjectsToHandles(userData).get()); |
| loadParameters.shouldTreatAsContinuingLoad = shouldTreatAsContinuingLoad; |
| loadParameters.websitePolicies = WTFMove(websitePolicies); |
| loadParameters.lockHistory = navigation.lockHistory(); |
| loadParameters.lockBackForwardList = navigation.lockBackForwardList(); |
| loadParameters.clientRedirectSourceForHistory = navigation.clientRedirectSourceForHistory(); |
| loadParameters.effectiveSandboxFlags = navigation.effectiveSandboxFlags(); |
| loadParameters.isNavigatingToAppBoundDomain = isNavigatingToAppBoundDomain; |
| loadParameters.existingNetworkResourceLoadIdentifierToResume = existingNetworkResourceLoadIdentifierToResume; |
| #if ENABLE(PUBLIC_SUFFIX_LIST) |
| loadParameters.topPrivatelyControlledDomain = WebCore::topPrivatelyControlledDomain(loadParameters.request.url().host().toString()); |
| #endif |
| maybeInitializeSandboxExtensionHandle(process, url, m_pageLoadState.resourceDirectoryURL(), loadParameters.sandboxExtensionHandle); |
| |
| addPlatformLoadParameters(process, loadParameters); |
| |
| if (shouldTreatAsContinuingLoad == ShouldTreatAsContinuingLoad::No) |
| preconnectTo(url, predictedUserAgentForRequest(loadParameters.request)); |
| |
| navigation.setIsLoadedWithNavigationShared(true); |
| |
| process->markProcessAsRecentlyUsed(); |
| |
| if (!process->isLaunching() || !url.isLocalFile()) |
| process->send(Messages::WebPage::LoadRequest(loadParameters), webPageID); |
| else |
| process->send(Messages::WebPage::LoadRequestWaitingForProcessLaunch(loadParameters, m_pageLoadState.resourceDirectoryURL(), m_identifier, true), webPageID); |
| process->startResponsivenessTimer(); |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::loadFile(const String& fileURLString, const String& resourceDirectoryURLString, bool isAppInitiated, API::Object* userData) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadFile:"); |
| |
| if (m_isClosed) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadFile: page is closed"); |
| return nullptr; |
| } |
| |
| #if PLATFORM(MAC) |
| if (isQuarantinedAndNotUserApproved(fileURLString)) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadFile: file cannot be opened because it is from an unidentified developer."); |
| return nullptr; |
| } |
| #endif |
| |
| if (!hasRunningProcess()) |
| launchProcess({ }, ProcessLaunchReason::InitialProcess); |
| |
| URL fileURL { fileURLString }; |
| if (!fileURL.isLocalFile()) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadFile: file is not local"); |
| return nullptr; |
| } |
| |
| URL resourceDirectoryURL; |
| if (resourceDirectoryURLString.isNull()) |
| resourceDirectoryURL = URL({ }, "file:///"_s); |
| else { |
| resourceDirectoryURL = URL { resourceDirectoryURLString }; |
| if (!resourceDirectoryURL.isLocalFile()) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadFile: resource URL is not local"); |
| return nullptr; |
| } |
| } |
| |
| auto navigation = m_navigationState->createLoadRequestNavigation(ResourceRequest(fileURL), m_backForwardList->currentItem()); |
| |
| if (shouldForceForegroundPriorityForClientNavigation()) |
| navigation->setClientNavigationActivity(process().throttler().foregroundActivity("Client navigation"_s)); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| m_pageLoadState.setPendingAPIRequest(transaction, { navigation->navigationID(), fileURLString }, resourceDirectoryURL); |
| |
| auto request = ResourceRequest(fileURL); |
| request.setIsAppInitiated(isAppInitiated); |
| m_lastNavigationWasAppInitiated = isAppInitiated; |
| |
| LoadParameters loadParameters; |
| loadParameters.navigationID = navigation->navigationID(); |
| loadParameters.request = WTFMove(request); |
| loadParameters.shouldOpenExternalURLsPolicy = ShouldOpenExternalURLsPolicy::ShouldNotAllow; |
| loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); |
| #if ENABLE(PUBLIC_SUFFIX_LIST) |
| loadParameters.topPrivatelyControlledDomain = WebCore::topPrivatelyControlledDomain(loadParameters.request.url().host().toString()); |
| #endif |
| const bool checkAssumedReadAccessToResourceURL = false; |
| maybeInitializeSandboxExtensionHandle(m_process, fileURL, resourceDirectoryURL, loadParameters.sandboxExtensionHandle, checkAssumedReadAccessToResourceURL); |
| addPlatformLoadParameters(m_process, loadParameters); |
| |
| m_process->markProcessAsRecentlyUsed(); |
| if (m_process->isLaunching()) |
| send(Messages::WebPage::LoadRequestWaitingForProcessLaunch(loadParameters, resourceDirectoryURL, m_identifier, checkAssumedReadAccessToResourceURL)); |
| else |
| send(Messages::WebPage::LoadRequest(loadParameters)); |
| m_process->startResponsivenessTimer(); |
| |
| return navigation; |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::loadData(const IPC::DataReference& data, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadData:"); |
| |
| #if ENABLE(APP_BOUND_DOMAINS) |
| if (MIMEType == "text/html"_s && !isFullWebBrowser()) |
| m_limitsNavigationsToAppBoundDomains = true; |
| #endif |
| |
| if (m_isClosed) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadData: page is closed"); |
| return nullptr; |
| } |
| |
| if (!hasRunningProcess()) |
| launchProcess({ }, ProcessLaunchReason::InitialProcess); |
| |
| auto navigation = m_navigationState->createLoadDataNavigation(makeUnique<API::SubstituteData>(Vector(data), MIMEType, encoding, baseURL, userData)); |
| |
| if (shouldForceForegroundPriorityForClientNavigation()) |
| navigation->setClientNavigationActivity(process().throttler().foregroundActivity("Client navigation"_s)); |
| |
| loadDataWithNavigationShared(m_process.copyRef(), m_webPageID, navigation, data, MIMEType, encoding, baseURL, userData, ShouldTreatAsContinuingLoad::No, isNavigatingToAppBoundDomain(), std::nullopt, shouldOpenExternalURLsPolicy, SubstituteData::SessionHistoryVisibility::Hidden); |
| return navigation; |
| } |
| |
| void WebPageProxy::loadDataWithNavigationShared(Ref<WebProcessProxy>&& process, WebCore::PageIdentifier webPageID, API::Navigation& navigation, const IPC::DataReference& data, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, ShouldTreatAsContinuingLoad shouldTreatAsContinuingLoad, std::optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain, std::optional<WebsitePoliciesData>&& websitePolicies, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, SubstituteData::SessionHistoryVisibility sessionHistoryVisibility) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadDataWithNavigation"); |
| |
| ASSERT(!m_isClosed); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| m_pageLoadState.setPendingAPIRequest(transaction, { navigation.navigationID(), !baseURL.isEmpty() ? baseURL : aboutBlankURL().string() }); |
| |
| LoadParameters loadParameters; |
| loadParameters.sessionHistoryVisibility = sessionHistoryVisibility; |
| loadParameters.navigationID = navigation.navigationID(); |
| loadParameters.data = data; |
| loadParameters.MIMEType = MIMEType; |
| loadParameters.encodingName = encoding; |
| loadParameters.baseURLString = baseURL; |
| loadParameters.shouldTreatAsContinuingLoad = shouldTreatAsContinuingLoad; |
| loadParameters.userData = UserData(process->transformObjectsToHandles(userData).get()); |
| loadParameters.websitePolicies = WTFMove(websitePolicies); |
| loadParameters.shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicy; |
| loadParameters.isNavigatingToAppBoundDomain = isNavigatingToAppBoundDomain; |
| loadParameters.isServiceWorkerLoad = isServiceWorkerPage(); |
| addPlatformLoadParameters(process, loadParameters); |
| |
| process->markProcessAsRecentlyUsed(); |
| process->assumeReadAccessToBaseURL(*this, baseURL); |
| process->send(Messages::WebPage::LoadData(loadParameters), webPageID); |
| process->startResponsivenessTimer(); |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::loadSimulatedRequest(WebCore::ResourceRequest&& simulatedRequest, WebCore::ResourceResponse&& simulatedResponse, const IPC::DataReference& data) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadSimulatedRequest:"); |
| |
| #if PLATFORM(COCOA) |
| setLastNavigationWasAppInitiated(simulatedRequest); |
| #endif |
| |
| #if ENABLE(APP_BOUND_DOMAINS) |
| if (simulatedResponse.mimeType() == "text/html"_s && !isFullWebBrowser()) |
| m_limitsNavigationsToAppBoundDomains = true; |
| #endif |
| |
| if (m_isClosed) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadSimulatedRequest: page is closed"); |
| return nullptr; |
| } |
| |
| if (!hasRunningProcess()) |
| launchProcess(RegistrableDomain { simulatedRequest.url() }, ProcessLaunchReason::InitialProcess); |
| |
| auto navigation = m_navigationState->createSimulatedLoadWithDataNavigation(ResourceRequest(simulatedRequest), makeUnique<API::SubstituteData>(Vector(data), ResourceResponse(simulatedResponse), WebCore::SubstituteData::SessionHistoryVisibility::Visible), m_backForwardList->currentItem()); |
| |
| if (shouldForceForegroundPriorityForClientNavigation()) |
| navigation->setClientNavigationActivity(process().throttler().foregroundActivity("Client navigation"_s)); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| auto baseURL = simulatedRequest.url().string(); |
| simulatedResponse.setURL(simulatedRequest.url()); // These should always match for simulated load |
| |
| m_pageLoadState.setPendingAPIRequest(transaction, { navigation->navigationID(), !baseURL.isEmpty() ? baseURL : aboutBlankURL().string() }); |
| |
| LoadParameters loadParameters; |
| loadParameters.navigationID = navigation->navigationID(); |
| loadParameters.request = WTFMove(simulatedRequest); |
| loadParameters.data = data; |
| loadParameters.MIMEType = simulatedResponse.mimeType(); |
| loadParameters.encodingName = simulatedResponse.textEncodingName(); |
| loadParameters.baseURLString = baseURL; |
| loadParameters.shouldOpenExternalURLsPolicy = WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow; |
| loadParameters.shouldTreatAsContinuingLoad = ShouldTreatAsContinuingLoad::No; |
| loadParameters.lockHistory = navigation->lockHistory(); |
| loadParameters.lockBackForwardList = navigation->lockBackForwardList(); |
| loadParameters.clientRedirectSourceForHistory = navigation->clientRedirectSourceForHistory(); |
| loadParameters.effectiveSandboxFlags = navigation->effectiveSandboxFlags(); |
| loadParameters.isNavigatingToAppBoundDomain = isNavigatingToAppBoundDomain(); |
| |
| simulatedResponse.setExpectedContentLength(data.size()); |
| simulatedResponse.includeCertificateInfo(); |
| |
| addPlatformLoadParameters(m_process, loadParameters); |
| |
| m_process->markProcessAsRecentlyUsed(); |
| m_process->assumeReadAccessToBaseURL(*this, baseURL); |
| m_process->send(Messages::WebPage::LoadSimulatedRequestAndResponse(loadParameters, simulatedResponse), m_webPageID); |
| m_process->startResponsivenessTimer(); |
| return navigation; |
| } |
| |
| void WebPageProxy::loadAlternateHTML(const IPC::DataReference& htmlData, const String& encoding, const URL& baseURL, const URL& unreachableURL, API::Object* userData) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadAlternateHTML"); |
| |
| // When the UIProcess is in the process of handling a failing provisional load, do not attempt to |
| // start a second alternative HTML load as this will prevent the page load state from being |
| // handled properly. |
| if (m_isClosed || m_isLoadingAlternateHTMLStringForFailingProvisionalLoad) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadAlternateHTML: page is closed (or other)"); |
| return; |
| } |
| |
| if (!m_failingProvisionalLoadURL.isEmpty()) |
| m_isLoadingAlternateHTMLStringForFailingProvisionalLoad = true; |
| |
| if (!hasRunningProcess()) |
| launchProcess(RegistrableDomain { baseURL }, ProcessLaunchReason::InitialProcess); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| m_pageLoadState.setPendingAPIRequest(transaction, { 0, unreachableURL.string() }); |
| m_pageLoadState.setUnreachableURL(transaction, unreachableURL.string()); |
| |
| if (m_mainFrame) |
| m_mainFrame->setUnreachableURL(unreachableURL); |
| |
| LoadParameters loadParameters; |
| loadParameters.navigationID = 0; |
| loadParameters.data = htmlData; |
| loadParameters.MIMEType = "text/html"_s; |
| loadParameters.encodingName = encoding; |
| loadParameters.baseURLString = baseURL.string(); |
| loadParameters.unreachableURLString = unreachableURL.string(); |
| loadParameters.provisionalLoadErrorURLString = m_failingProvisionalLoadURL; |
| loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); |
| addPlatformLoadParameters(process(), loadParameters); |
| |
| m_process->markProcessAsRecentlyUsed(); |
| m_process->assumeReadAccessToBaseURL(*this, baseURL.string()); |
| m_process->assumeReadAccessToBaseURL(*this, unreachableURL.string()); |
| send(Messages::WebPage::LoadAlternateHTML(loadParameters)); |
| m_process->startResponsivenessTimer(); |
| } |
| |
| void WebPageProxy::loadWebArchiveData(API::Data* webArchiveData, API::Object* userData) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadWebArchiveData:"); |
| |
| if (m_isClosed) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadWebArchiveData: page is closed"); |
| return; |
| } |
| |
| if (!hasRunningProcess()) |
| launchProcess({ }, ProcessLaunchReason::InitialProcess); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setPendingAPIRequest(transaction, { 0, aboutBlankURL().string() }); |
| |
| LoadParameters loadParameters; |
| loadParameters.navigationID = 0; |
| loadParameters.data = webArchiveData->dataReference(); |
| loadParameters.MIMEType = "application/x-webarchive"_s; |
| loadParameters.encodingName = "utf-16"_s; |
| loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); |
| addPlatformLoadParameters(process(), loadParameters); |
| |
| m_process->markProcessAsRecentlyUsed(); |
| send(Messages::WebPage::LoadData(loadParameters)); |
| m_process->startResponsivenessTimer(); |
| } |
| |
| void WebPageProxy::navigateToPDFLinkWithSimulatedClick(const String& urlString, IntPoint documentPoint, IntPoint screenPoint) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "navigateToPDFLinkWithSimulatedClick:"); |
| |
| if (m_isClosed) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "navigateToPDFLinkWithSimulatedClick: page is closed:"); |
| return; |
| } |
| |
| if (WTF::protocolIsJavaScript(urlString)) |
| return; |
| |
| if (!hasRunningProcess()) |
| launchProcess(RegistrableDomain { URL { urlString } }, ProcessLaunchReason::InitialProcess); |
| |
| send(Messages::WebPage::NavigateToPDFLinkWithSimulatedClick(urlString, documentPoint, screenPoint)); |
| m_process->startResponsivenessTimer(); |
| } |
| |
| void WebPageProxy::stopLoading() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "stopLoading:"); |
| |
| if (!hasRunningProcess()) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "navigateToPDFLinkWithSimulatedClick: page is not valid"); |
| return; |
| } |
| |
| send(Messages::WebPage::StopLoading()); |
| if (m_provisionalPage) { |
| m_provisionalPage->cancel(); |
| m_provisionalPage = nullptr; |
| } |
| m_process->startResponsivenessTimer(); |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::reload(OptionSet<WebCore::ReloadOption> options) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "reload:"); |
| |
| // Make sure the Network & GPU processes are still responsive. This is so that reload() gets us out of the bad state if one of these |
| // processes is hung. |
| websiteDataStore().networkProcess().checkForResponsiveness(); |
| #if ENABLE(GPU_PROCESS) |
| if (auto* gpuProcess = process().processPool().gpuProcess()) |
| gpuProcess->checkForResponsiveness(); |
| #endif |
| |
| SandboxExtension::Handle sandboxExtensionHandle; |
| |
| String url = currentURL(); |
| if (!url.isEmpty()) { |
| // We may not have an extension yet if back/forward list was reinstated after a WebProcess crash or a browser relaunch |
| maybeInitializeSandboxExtensionHandle(m_process, URL { url }, currentResourceDirectoryURL(), sandboxExtensionHandle); |
| } |
| |
| if (!hasRunningProcess()) |
| return launchProcessForReload(); |
| |
| auto navigation = m_navigationState->createReloadNavigation(m_backForwardList->currentItem()); |
| |
| if (!url.isEmpty()) { |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setPendingAPIRequest(transaction, { navigation->navigationID(), url }); |
| } |
| |
| // Store decision to reload without content blockers on the navigation so that we can later set the corresponding |
| // WebsitePolicies flag in WebPageProxy::receivedNavigationPolicyDecision(). |
| if (options.contains(WebCore::ReloadOption::DisableContentBlockers)) |
| navigation->setUserContentExtensionsEnabled(false); |
| |
| m_process->markProcessAsRecentlyUsed(); |
| send(Messages::WebPage::Reload(navigation->navigationID(), options.toRaw(), sandboxExtensionHandle)); |
| m_process->startResponsivenessTimer(); |
| |
| #if ENABLE(SPEECH_SYNTHESIS) |
| resetSpeechSynthesizer(); |
| #endif |
| |
| return navigation; |
| } |
| |
| void WebPageProxy::recordAutomaticNavigationSnapshot() |
| { |
| if (m_shouldSuppressNextAutomaticNavigationSnapshot) |
| return; |
| |
| if (WebBackForwardListItem* item = m_backForwardList->currentItem()) |
| recordNavigationSnapshot(*item); |
| } |
| |
| void WebPageProxy::recordNavigationSnapshot(WebBackForwardListItem& item) |
| { |
| if (!m_shouldRecordNavigationSnapshots) |
| return; |
| |
| #if PLATFORM(COCOA) || PLATFORM(GTK) |
| ViewSnapshotStore::singleton().recordSnapshot(*this, item); |
| #else |
| UNUSED_PARAM(item); |
| #endif |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::goForward() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "goForward:"); |
| auto* forwardItem = m_backForwardList->goForwardItemSkippingItemsWithoutUserGesture(); |
| if (!forwardItem) |
| return nullptr; |
| |
| return goToBackForwardItem(*forwardItem, FrameLoadType::Forward); |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::goBack() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "goBack:"); |
| auto* backItem = m_backForwardList->goBackItemSkippingItemsWithoutUserGesture(); |
| if (!backItem) |
| return nullptr; |
| |
| return goToBackForwardItem(*backItem, FrameLoadType::Back); |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::goToBackForwardItem(WebBackForwardListItem& item) |
| { |
| return goToBackForwardItem(item, FrameLoadType::IndexedBackForward); |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::goToBackForwardItem(WebBackForwardListItem& item, FrameLoadType frameLoadType) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "goToBackForwardItem:"); |
| LOG(Loading, "WebPageProxy %p goToBackForwardItem to item URL %s", this, item.url().utf8().data()); |
| |
| if (m_isClosed) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "goToBackForwardItem: page is closed"); |
| return nullptr; |
| } |
| |
| if (!hasRunningProcess()) { |
| launchProcess(RegistrableDomain { URL { item.url() } }, ProcessLaunchReason::InitialProcess); |
| |
| if (&item != m_backForwardList->currentItem()) |
| m_backForwardList->goToItem(item); |
| } |
| |
| RefPtr<API::Navigation> navigation; |
| if (!m_backForwardList->currentItem()->itemIsInSameDocument(item)) |
| navigation = m_navigationState->createBackForwardNavigation(item, m_backForwardList->currentItem(), frameLoadType); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setPendingAPIRequest(transaction, { navigation ? navigation->navigationID() : 0, item.url() }); |
| |
| m_process->markProcessAsRecentlyUsed(); |
| send(Messages::WebPage::GoToBackForwardItem(navigation ? navigation->navigationID() : 0, item.itemID(), frameLoadType, ShouldTreatAsContinuingLoad::No, std::nullopt, m_lastNavigationWasAppInitiated, std::nullopt)); |
| m_process->startResponsivenessTimer(); |
| |
| return navigation; |
| } |
| |
| void WebPageProxy::tryRestoreScrollPosition() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "tryRestoreScrollPosition:"); |
| |
| if (!hasRunningProcess()) { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "tryRestoreScrollPosition: page is not valid"); |
| return; |
| } |
| |
| send(Messages::WebPage::TryRestoreScrollPosition()); |
| } |
| |
| void WebPageProxy::didChangeBackForwardList(WebBackForwardListItem* added, Vector<Ref<WebBackForwardListItem>>&& removed) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| if (!m_navigationClient->didChangeBackForwardList(*this, added, removed) && m_loaderClient) |
| m_loaderClient->didChangeBackForwardList(*this, added, WTFMove(removed)); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| m_pageLoadState.setCanGoBack(transaction, m_backForwardList->backItem()); |
| m_pageLoadState.setCanGoForward(transaction, m_backForwardList->forwardItem()); |
| } |
| |
| void WebPageProxy::willGoToBackForwardListItem(const BackForwardItemIdentifier& itemID, bool inBackForwardCache) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| if (auto* item = m_backForwardList->itemForID(itemID)) |
| m_navigationClient->willGoToBackForwardListItem(*this, *item, inBackForwardCache); |
| } |
| |
| bool WebPageProxy::shouldKeepCurrentBackForwardListItemInList(WebBackForwardListItem& item) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| return !m_loaderClient || m_loaderClient->shouldKeepCurrentBackForwardListItemInList(*this, item); |
| } |
| |
| bool WebPageProxy::canShowMIMEType(const String& mimeType) |
| { |
| if (MIMETypeRegistry::canShowMIMEType(mimeType)) |
| return true; |
| |
| if (m_preferences->pdfJSViewerEnabled() && MIMETypeRegistry::isPDFMIMEType(mimeType)) |
| return true; |
| |
| #if PLATFORM(COCOA) |
| // On Mac, we can show PDFs. |
| if (MIMETypeRegistry::isPDFOrPostScriptMIMEType(mimeType) && !WebProcessPool::omitPDFSupport()) |
| return true; |
| #endif // PLATFORM(COCOA) |
| |
| return false; |
| } |
| |
| void WebPageProxy::setControlledByAutomation(bool controlled) |
| { |
| if (m_controlledByAutomation == controlled) |
| return; |
| |
| m_controlledByAutomation = controlled; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetControlledByAutomation(controlled)); |
| websiteDataStore().networkProcess().send(Messages::NetworkProcess::SetSessionIsControlledByAutomation(m_websiteDataStore->sessionID(), m_controlledByAutomation), 0); |
| } |
| |
| void WebPageProxy::createInspectorTarget(const String& targetId, Inspector::InspectorTargetType type) |
| { |
| MESSAGE_CHECK(m_process, !targetId.isEmpty()); |
| m_inspectorController->createInspectorTarget(targetId, type); |
| } |
| |
| void WebPageProxy::destroyInspectorTarget(const String& targetId) |
| { |
| MESSAGE_CHECK(m_process, !targetId.isEmpty()); |
| m_inspectorController->destroyInspectorTarget(targetId); |
| } |
| |
| void WebPageProxy::sendMessageToInspectorFrontend(const String& targetId, const String& message) |
| { |
| m_inspectorController->sendMessageToInspectorFrontend(targetId, message); |
| } |
| |
| #if ENABLE(REMOTE_INSPECTOR) |
| void WebPageProxy::setIndicating(bool indicating) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetIndicating(indicating)); |
| } |
| |
| bool WebPageProxy::allowsRemoteInspection() const |
| { |
| return m_inspectorDebuggable->remoteDebuggingAllowed(); |
| } |
| |
| void WebPageProxy::setAllowsRemoteInspection(bool allow) |
| { |
| m_inspectorDebuggable->setRemoteDebuggingAllowed(allow); |
| } |
| |
| String WebPageProxy::remoteInspectionNameOverride() const |
| { |
| return m_inspectorDebuggable->nameOverride(); |
| } |
| |
| void WebPageProxy::setRemoteInspectionNameOverride(const String& name) |
| { |
| m_inspectorDebuggable->setNameOverride(name); |
| } |
| |
| void WebPageProxy::remoteInspectorInformationDidChange() |
| { |
| m_inspectorDebuggable->update(); |
| } |
| #endif |
| |
| void WebPageProxy::setBackgroundColor(const std::optional<Color>& color) |
| { |
| if (m_backgroundColor == color) |
| return; |
| |
| m_backgroundColor = color; |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetBackgroundColor(color)); |
| } |
| |
| void WebPageProxy::setTopContentInset(float contentInset) |
| { |
| if (m_topContentInset == contentInset) |
| return; |
| |
| m_topContentInset = contentInset; |
| |
| pageClient().topContentInsetDidChange(); |
| |
| if (!hasRunningProcess()) |
| return; |
| #if PLATFORM(COCOA) |
| send(Messages::WebPage::SetTopContentInsetFenced(contentInset, m_drawingArea->createFence())); |
| #else |
| send(Messages::WebPage::SetTopContentInset(contentInset)); |
| #endif |
| } |
| |
| void WebPageProxy::setUnderlayColor(const Color& color) |
| { |
| if (m_underlayColor == color) |
| return; |
| |
| m_underlayColor = color; |
| |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetUnderlayColor(color)); |
| } |
| |
| Color WebPageProxy::underPageBackgroundColor() const |
| { |
| if (m_underPageBackgroundColorOverride.isValid()) |
| return m_underPageBackgroundColorOverride; |
| |
| if (m_pageExtendedBackgroundColor.isValid()) |
| return m_pageExtendedBackgroundColor; |
| |
| return platformUnderPageBackgroundColor(); |
| } |
| |
| void WebPageProxy::setUnderPageBackgroundColorOverride(Color&& newUnderPageBackgroundColorOverride) |
| { |
| if (newUnderPageBackgroundColorOverride == m_underPageBackgroundColorOverride) |
| return; |
| |
| auto oldUnderPageBackgroundColor = underPageBackgroundColor(); |
| auto oldUnderPageBackgroundColorOverride = std::exchange(m_underPageBackgroundColorOverride, newUnderPageBackgroundColorOverride); |
| bool changesUnderPageBackgroundColor = !equalIgnoringSemanticColor(oldUnderPageBackgroundColor, underPageBackgroundColor()); |
| m_underPageBackgroundColorOverride = WTFMove(oldUnderPageBackgroundColorOverride); |
| |
| if (changesUnderPageBackgroundColor) |
| pageClient().underPageBackgroundColorWillChange(); |
| |
| m_underPageBackgroundColorOverride = WTFMove(newUnderPageBackgroundColorOverride); |
| |
| if (changesUnderPageBackgroundColor) |
| pageClient().underPageBackgroundColorDidChange(); |
| |
| if (m_hasPendingUnderPageBackgroundColorOverrideToDispatch) |
| return; |
| |
| m_hasPendingUnderPageBackgroundColorOverrideToDispatch = true; |
| |
| RunLoop::main().dispatch([this, weakThis = WeakPtr { *this }] { |
| if (!weakThis) |
| return; |
| |
| if (!m_hasPendingUnderPageBackgroundColorOverrideToDispatch) |
| return; |
| |
| m_hasPendingUnderPageBackgroundColorOverrideToDispatch = false; |
| |
| if (m_pageClient) |
| m_pageClient->didChangeBackgroundColor(); |
| |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetUnderPageBackgroundColorOverride(m_underPageBackgroundColorOverride)); |
| }); |
| } |
| |
| void WebPageProxy::viewWillStartLiveResize() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| closeOverlayedViews(); |
| send(Messages::WebPage::ViewWillStartLiveResize()); |
| } |
| |
| void WebPageProxy::viewWillEndLiveResize() |
| { |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::ViewWillEndLiveResize()); |
| } |
| |
| void WebPageProxy::setViewNeedsDisplay(const Region& region) |
| { |
| pageClient().setViewNeedsDisplay(region); |
| } |
| |
| void WebPageProxy::requestScroll(const FloatPoint& scrollPosition, const IntPoint& scrollOrigin, ScrollIsAnimated animated) |
| { |
| pageClient().requestScroll(scrollPosition, scrollOrigin, animated); |
| } |
| |
| WebCore::FloatPoint WebPageProxy::viewScrollPosition() const |
| { |
| return pageClient().viewScrollPosition(); |
| } |
| |
| void WebPageProxy::setSuppressVisibilityUpdates(bool flag) |
| { |
| if (m_suppressVisibilityUpdates == flag) |
| return; |
| |
| WEBPAGEPROXY_RELEASE_LOG(ViewState, "setSuppressVisibilityUpdates: %d", flag); |
| m_suppressVisibilityUpdates = flag; |
| |
| if (!m_suppressVisibilityUpdates) { |
| #if PLATFORM(COCOA) |
| scheduleActivityStateUpdate(); |
| #else |
| dispatchActivityStateChange(); |
| #endif |
| } |
| } |
| |
| void WebPageProxy::updateActivityState(OptionSet<ActivityState::Flag> flagsToUpdate) |
| { |
| bool wasVisible = isViewVisible(); |
| m_activityState.remove(flagsToUpdate); |
| if (flagsToUpdate & ActivityState::IsFocused && pageClient().isViewFocused()) |
| m_activityState.add(ActivityState::IsFocused); |
| if (flagsToUpdate & ActivityState::WindowIsActive && pageClient().isViewWindowActive()) |
| m_activityState.add(ActivityState::WindowIsActive); |
| if (flagsToUpdate & ActivityState::IsVisible) { |
| bool isNowVisible = pageClient().isViewVisible(); |
| if (isNowVisible) |
| m_activityState.add(ActivityState::IsVisible); |
| if (wasVisible != isNowVisible) |
| WEBPAGEPROXY_RELEASE_LOG(ViewState, "updateActivityState: view visibility state changed %d -> %d", wasVisible, isNowVisible); |
| } |
| if (flagsToUpdate & ActivityState::IsVisibleOrOccluded && pageClient().isViewVisibleOrOccluded()) |
| m_activityState.add(ActivityState::IsVisibleOrOccluded); |
| if (flagsToUpdate & ActivityState::IsInWindow && pageClient().isViewInWindow()) |
| m_activityState.add(ActivityState::IsInWindow); |
| bool isVisuallyIdle = pageClient().isVisuallyIdle(); |
| #if PLATFORM(COCOA) && !HAVE(CGS_FIX_FOR_RADAR_97530095) && ENABLE(MEDIA_USAGE) |
| if (pageClient().isViewVisible() && m_mediaUsageManager && m_mediaUsageManager->isPlayingVideoInViewport()) |
| isVisuallyIdle = false; |
| #endif |
| if (flagsToUpdate & ActivityState::IsVisuallyIdle && isVisuallyIdle) |
| m_activityState.add(ActivityState::IsVisuallyIdle); |
| if (flagsToUpdate & ActivityState::IsAudible && m_mediaState.contains(MediaProducerMediaState::IsPlayingAudio) && !(m_mutedState.contains(MediaProducerMutedState::AudioIsMuted))) |
| m_activityState.add(ActivityState::IsAudible); |
| if (flagsToUpdate & ActivityState::IsLoading && m_pageLoadState.isLoading()) |
| m_activityState.add(ActivityState::IsLoading); |
| if (flagsToUpdate & ActivityState::IsCapturingMedia && m_mediaState.containsAny({ MediaProducerMediaState::HasActiveAudioCaptureDevice, MediaProducerMediaState::HasActiveVideoCaptureDevice })) |
| m_activityState.add(ActivityState::IsCapturingMedia); |
| } |
| |
| void WebPageProxy::activityStateDidChange(OptionSet<ActivityState::Flag> mayHaveChanged, ActivityStateChangeDispatchMode dispatchMode, ActivityStateChangeReplyMode replyMode) |
| { |
| LOG_WITH_STREAM(ActivityState, stream << "WebPageProxy " << identifier() << " activityStateDidChange - mayHaveChanged " << mayHaveChanged); |
| |
| m_potentiallyChangedActivityStateFlags.add(mayHaveChanged); |
| m_activityStateChangeWantsSynchronousReply = m_activityStateChangeWantsSynchronousReply || replyMode == ActivityStateChangeReplyMode::Synchronous; |
| |
| // We need to do this here instead of inside dispatchActivityStateChange() or viewIsBecomingVisible() because these don't run when the view doesn't |
| // have a running WebProcess. For the same reason, we need to rely on PageClient::isViewVisible() instead of WebPageProxy::isViewVisible(). |
| if (m_potentiallyChangedActivityStateFlags & ActivityState::IsVisible && m_shouldReloadDueToCrashWhenVisible && pageClient().isViewVisible()) { |
| RunLoop::main().dispatch([this, weakThis = WeakPtr { *this }] { |
| if (weakThis && std::exchange(m_shouldReloadDueToCrashWhenVisible, false)) { |
| WEBPAGEPROXY_RELEASE_LOG(ViewState, "activityStateDidChange: view is becoming visible after a crash, attempt a reload"); |
| tryReloadAfterProcessTermination(); |
| } |
| }); |
| } |
| |
| if (m_suppressVisibilityUpdates && dispatchMode != ActivityStateChangeDispatchMode::Immediate) { |
| WEBPAGEPROXY_RELEASE_LOG(ViewState, "activityStateDidChange: Returning early due to m_suppressVisibilityUpdates"); |
| return; |
| } |
| |
| #if PLATFORM(COCOA) |
| bool isNewlyInWindow = !isInWindow() && (mayHaveChanged & ActivityState::IsInWindow) && pageClient().isViewInWindow(); |
| if (dispatchMode == ActivityStateChangeDispatchMode::Immediate || isNewlyInWindow) { |
| dispatchActivityStateChange(); |
| return; |
| } |
| scheduleActivityStateUpdate(); |
| #else |
| UNUSED_PARAM(dispatchMode); |
| dispatchActivityStateChange(); |
| #endif |
| } |
| |
| void WebPageProxy::viewDidLeaveWindow() |
| { |
| closeOverlayedViews(); |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| // When leaving the current page, close the video fullscreen. |
| if (m_videoFullscreenManager && m_videoFullscreenManager->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModeStandard)) |
| m_videoFullscreenManager->requestHideAndExitFullscreen(); |
| #endif |
| } |
| |
| void WebPageProxy::viewDidEnterWindow() |
| { |
| LayerHostingMode layerHostingMode = pageClient().viewLayerHostingMode(); |
| if (m_layerHostingMode != layerHostingMode) { |
| m_layerHostingMode = layerHostingMode; |
| send(Messages::WebPage::SetLayerHostingMode(layerHostingMode)); |
| } |
| } |
| |
| void WebPageProxy::dispatchActivityStateChange() |
| { |
| #if PLATFORM(COCOA) |
| if (m_activityStateChangeDispatcher->isScheduled()) |
| m_activityStateChangeDispatcher->invalidate(); |
| m_hasScheduledActivityStateUpdate = false; |
| #endif |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| LOG_WITH_STREAM(ActivityState, stream << "WebPageProxy " << identifier() << " dispatchActivityStateChange - potentiallyChangedActivityStateFlags " << m_potentiallyChangedActivityStateFlags); |
| |
| // If the visibility state may have changed, then so may the visually idle & occluded agnostic state. |
| if (m_potentiallyChangedActivityStateFlags & ActivityState::IsVisible) |
| m_potentiallyChangedActivityStateFlags.add({ ActivityState::IsVisibleOrOccluded, ActivityState::IsVisuallyIdle }); |
| |
| // Record the prior view state, update the flags that may have changed, |
| // and check which flags have actually changed. |
| auto previousActivityState = m_activityState; |
| updateActivityState(m_potentiallyChangedActivityStateFlags); |
| auto changed = m_activityState ^ previousActivityState; |
| |
| if (changed) |
| LOG_WITH_STREAM(ActivityState, stream << "WebPageProxy " << identifier() << " dispatchActivityStateChange: state changed from " << previousActivityState << " to " << m_activityState); |
| |
| if ((changed & ActivityState::WindowIsActive) && isViewWindowActive()) |
| updateCurrentModifierState(); |
| |
| if ((m_potentiallyChangedActivityStateFlags & ActivityState::IsVisible)) { |
| if (isViewVisible()) |
| viewIsBecomingVisible(); |
| else |
| m_process->pageIsBecomingInvisible(m_webPageID); |
| } |
| |
| #if PLATFORM(MAC) || PLATFORM(MACCATALYST) |
| if (m_potentiallyChangedActivityStateFlags & ActivityState::IsConnectedToHardwareConsole) |
| isConnectedToHardwareConsoleDidChange(); |
| #endif |
| |
| bool isNowInWindow = (changed & ActivityState::IsInWindow) && isInWindow(); |
| // We always want to wait for the Web process to reply if we've been in-window before and are coming back in-window. |
| if (m_viewWasEverInWindow && isNowInWindow) { |
| if (m_drawingArea->hasVisibleContent() && m_waitsForPaintAfterViewDidMoveToWindow && !m_shouldSkipWaitingForPaintAfterNextViewDidMoveToWindow) |
| m_activityStateChangeWantsSynchronousReply = true; |
| m_shouldSkipWaitingForPaintAfterNextViewDidMoveToWindow = false; |
| } |
| |
| // Don't wait synchronously if the view state is not visible. (This matters in particular on iOS, where a hidden page may be suspended.) |
| if (!(m_activityState & ActivityState::IsVisible)) |
| m_activityStateChangeWantsSynchronousReply = false; |
| |
| auto activityStateChangeID = m_activityStateChangeWantsSynchronousReply ? takeNextActivityStateChangeID() : static_cast<ActivityStateChangeID>(ActivityStateChangeAsynchronous); |
| |
| if (changed || activityStateChangeID != ActivityStateChangeAsynchronous || !m_nextActivityStateChangeCallbacks.isEmpty()) { |
| sendWithAsyncReply(Messages::WebPage::SetActivityState(m_activityState, activityStateChangeID), [callbacks = std::exchange(m_nextActivityStateChangeCallbacks, { })] () mutable { |
| for (auto& callback : callbacks) |
| callback(); |
| }); |
| } |
| |
| // This must happen after the SetActivityState message is sent, to ensure the page visibility event can fire. |
| updateThrottleState(); |
| |
| #if ENABLE(POINTER_LOCK) |
| if (((changed & ActivityState::IsVisible) && !isViewVisible()) || ((changed & ActivityState::WindowIsActive) && !pageClient().isViewWindowActive()) |
| || ((changed & ActivityState::IsFocused) && !(m_activityState & ActivityState::IsFocused))) |
| requestPointerUnlock(); |
| #endif |
| |
| if (changed & ActivityState::IsVisible) { |
| if (isViewVisible()) |
| m_visiblePageToken = m_process->visiblePageToken(); |
| else { |
| m_visiblePageToken = nullptr; |
| |
| // If we've started the responsiveness timer as part of telling the web process to update the backing store |
| // state, it might not send back a reply (since it won't paint anything if the web page is hidden) so we |
| // stop the unresponsiveness timer here. |
| m_process->stopResponsivenessTimer(); |
| } |
| } |
| |
| if (changed & ActivityState::IsInWindow) { |
| if (isInWindow()) |
| viewDidEnterWindow(); |
| else |
| viewDidLeaveWindow(); |
| } |
| |
| updateBackingStoreDiscardableState(); |
| |
| if (activityStateChangeID != ActivityStateChangeAsynchronous) |
| waitForDidUpdateActivityState(activityStateChangeID); |
| |
| m_potentiallyChangedActivityStateFlags = { }; |
| m_activityStateChangeWantsSynchronousReply = false; |
| m_viewWasEverInWindow |= isNowInWindow; |
| |
| #if PLATFORM(COCOA) |
| for (auto& callback : m_activityStateUpdateCallbacks) |
| callback(); |
| m_activityStateUpdateCallbacks.clear(); |
| #endif |
| } |
| |
| void WebPageProxy::updateThrottleState() |
| { |
| bool processSuppressionEnabled = m_preferences->pageVisibilityBasedProcessSuppressionEnabled(); |
| |
| // If process suppression is not enabled take a token on the process pool to disable suppression of support processes. |
| if (!processSuppressionEnabled) |
| m_preventProcessSuppressionCount = m_process->processPool().processSuppressionDisabledForPageCount(); |
| else if (!m_preventProcessSuppressionCount) |
| m_preventProcessSuppressionCount = nullptr; |
| |
| if (m_activityState & ActivityState::IsVisuallyIdle) |
| m_pageIsUserObservableCount = nullptr; |
| else if (!m_pageIsUserObservableCount) |
| m_pageIsUserObservableCount = m_process->processPool().userObservablePageCount(); |
| |
| #if PLATFORM(IOS_FAMILY) |
| if (isViewVisible()) { |
| if (!m_isVisibleActivity || !m_isVisibleActivity->isValid()) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSuspension, "updateThrottleState: UIProcess is taking a foreground assertion because the view is visible"); |
| m_isVisibleActivity = m_process->throttler().foregroundActivity("View is visible"_s).moveToUniquePtr(); |
| } |
| } else if (m_isVisibleActivity) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSuspension, "updateThrottleState: UIProcess is releasing a foreground assertion because the view is no longer visible"); |
| m_isVisibleActivity = nullptr; |
| } |
| |
| bool isAudible = m_activityState.contains(ActivityState::IsAudible); |
| if (isAudible) { |
| if (!m_isAudibleActivity || !m_isAudibleActivity->isValid()) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSuspension, "updateThrottleState: UIProcess is taking a foreground assertion because we are playing audio"); |
| m_isAudibleActivity = m_process->throttler().foregroundActivity("View is playing audio"_s).moveToUniquePtr(); |
| } |
| m_audibleActivityTimer.stop(); |
| } else if (m_isAudibleActivity) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSuspension, "updateThrottleState: UIProcess will release a foreground assertion in %g seconds because we are no longer playing audio", audibleActivityClearDelay.seconds()); |
| if (!m_audibleActivityTimer.isActive()) |
| m_audibleActivityTimer.startOneShot(audibleActivityClearDelay); |
| } |
| |
| bool isCapturingMedia = m_activityState.contains(ActivityState::IsCapturingMedia); |
| if (isCapturingMedia) { |
| if (!m_isCapturingActivity || !m_isCapturingActivity->isValid()) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSuspension, "updateThrottleState: UIProcess is taking a foreground assertion because media capture is active"); |
| m_isCapturingActivity = m_process->throttler().foregroundActivity("View is capturing media"_s).moveToUniquePtr(); |
| } |
| } else if (m_isCapturingActivity) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSuspension, "updateThrottleState: UIProcess is releasing a foreground assertion because media capture is no longer active"); |
| m_isCapturingActivity = nullptr; |
| } |
| #endif |
| } |
| |
| #if PLATFORM(IOS_FAMILY) |
| void WebPageProxy::clearAudibleActivity() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSuspension, "updateThrottleState: UIProcess is releasing a foreground assertion because we are no longer playing audio"); |
| m_isAudibleActivity = nullptr; |
| } |
| #endif |
| |
| void WebPageProxy::updateHiddenPageThrottlingAutoIncreases() |
| { |
| if (!m_preferences->hiddenPageDOMTimerThrottlingAutoIncreases()) |
| m_hiddenPageDOMTimerThrottlingAutoIncreasesCount = nullptr; |
| else if (!m_hiddenPageDOMTimerThrottlingAutoIncreasesCount) |
| m_hiddenPageDOMTimerThrottlingAutoIncreasesCount = m_process->processPool().hiddenPageThrottlingAutoIncreasesCount(); |
| } |
| |
| void WebPageProxy::layerHostingModeDidChange() |
| { |
| LayerHostingMode layerHostingMode = pageClient().viewLayerHostingMode(); |
| if (m_layerHostingMode == layerHostingMode) |
| return; |
| |
| m_layerHostingMode = layerHostingMode; |
| |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetLayerHostingMode(layerHostingMode)); |
| } |
| |
| void WebPageProxy::waitForDidUpdateActivityState(ActivityStateChangeID activityStateChangeID) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| if (m_process->state() != WebProcessProxy::State::Running) |
| return; |
| |
| // If we have previously timed out with no response from the WebProcess, don't block the UIProcess again until it starts responding. |
| if (m_waitingForDidUpdateActivityState) |
| return; |
| |
| #if PLATFORM(IOS_FAMILY) |
| // Hail Mary check. Should not be possible (dispatchActivityStateChange should force async if not visible, |
| // and if visible we should be holding an assertion) - but we should never block on a suspended process. |
| if (!m_isVisibleActivity) { |
| ASSERT_NOT_REACHED(); |
| return; |
| } |
| #endif |
| |
| m_waitingForDidUpdateActivityState = true; |
| |
| m_drawingArea->waitForDidUpdateActivityState(activityStateChangeID); |
| } |
| |
| IntSize WebPageProxy::viewSize() const |
| { |
| return pageClient().viewSize(); |
| } |
| |
| void WebPageProxy::setInitialFocus(bool forward, bool isKeyboardEventValid, const WebKeyboardEvent& keyboardEvent, CompletionHandler<void()>&& callbackFunction) |
| { |
| if (!hasRunningProcess()) { |
| callbackFunction(); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::SetInitialFocus(forward, isKeyboardEventValid, keyboardEvent), [callbackFunction = WTFMove(callbackFunction), backgroundActivity = m_process->throttler().backgroundActivity("WebPageProxy::setInitialFocus"_s)] () mutable { |
| callbackFunction(); |
| }); |
| } |
| |
| void WebPageProxy::clearSelection() |
| { |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::ClearSelection()); |
| } |
| |
| void WebPageProxy::restoreSelectionInFocusedEditableElement() |
| { |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::RestoreSelectionInFocusedEditableElement()); |
| } |
| |
| void WebPageProxy::validateCommand(const String& commandName, CompletionHandler<void(bool, int32_t)>&& callbackFunction) |
| { |
| if (!hasRunningProcess()) |
| return callbackFunction(false, 0); |
| |
| sendWithAsyncReply(Messages::WebPage::ValidateCommand(commandName), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::increaseListLevel() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::IncreaseListLevel()); |
| } |
| |
| void WebPageProxy::decreaseListLevel() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::DecreaseListLevel()); |
| } |
| |
| void WebPageProxy::changeListType() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ChangeListType()); |
| } |
| |
| void WebPageProxy::setBaseWritingDirection(WritingDirection direction) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetBaseWritingDirection(direction)); |
| } |
| |
| void WebPageProxy::updateFontAttributesAfterEditorStateChange() |
| { |
| m_cachedFontAttributesAtSelectionStart.reset(); |
| |
| if (m_editorState.isMissingPostLayoutData) |
| return; |
| |
| if (auto fontAttributes = m_editorState.postLayoutData().fontAttributes) { |
| m_uiClient->didChangeFontAttributes(*fontAttributes); |
| m_cachedFontAttributesAtSelectionStart = WTFMove(fontAttributes); |
| } |
| } |
| |
| void WebPageProxy::setNeedsFontAttributes(bool needsFontAttributes) |
| { |
| if (m_needsFontAttributes == needsFontAttributes) |
| return; |
| |
| m_needsFontAttributes = needsFontAttributes; |
| |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetNeedsFontAttributes(needsFontAttributes)); |
| } |
| |
| bool WebPageProxy::maintainsInactiveSelection() const |
| { |
| // Regardless of what the client wants to do, keep selections if a local Inspector is open. |
| // Otherwise, there is no way to use the console to inspect the state of a selection. |
| if (inspector() && inspector()->isVisible()) |
| return true; |
| |
| return m_maintainsInactiveSelection; |
| } |
| |
| void WebPageProxy::setMaintainsInactiveSelection(bool newValue) |
| { |
| m_maintainsInactiveSelection = newValue; |
| } |
| |
| void WebPageProxy::scheduleFullEditorStateUpdate() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ScheduleFullEditorStateUpdate()); |
| } |
| |
| void WebPageProxy::selectAll() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SelectAll()); |
| } |
| |
| static std::optional<DOMPasteAccessCategory> pasteAccessCategoryForCommand(const String& commandName) |
| { |
| static NeverDestroyed<HashMap<String, DOMPasteAccessCategory, ASCIICaseInsensitiveHash>> pasteCommandNames = HashMap<String, DOMPasteAccessCategory, ASCIICaseInsensitiveHash> { |
| { "Paste"_s, DOMPasteAccessCategory::General }, |
| { "PasteAndMatchStyle"_s, DOMPasteAccessCategory::General }, |
| { "PasteAsQuotation"_s, DOMPasteAccessCategory::General }, |
| { "PasteAsPlainText"_s, DOMPasteAccessCategory::General }, |
| { "PasteFont"_s, DOMPasteAccessCategory::Fonts }, |
| }; |
| |
| auto it = pasteCommandNames->find(commandName); |
| if (it != pasteCommandNames->end()) |
| return it->value; |
| |
| return std::nullopt; |
| } |
| |
| void WebPageProxy::executeEditCommand(const String& commandName, const String& argument, CompletionHandler<void()>&& callbackFunction) |
| { |
| if (!hasRunningProcess()) { |
| callbackFunction(); |
| return; |
| } |
| |
| if (auto pasteAccessCategory = pasteAccessCategoryForCommand(commandName)) |
| willPerformPasteCommand(*pasteAccessCategory); |
| |
| sendWithAsyncReply(Messages::WebPage::ExecuteEditCommandWithCallback(commandName, argument), [callbackFunction = WTFMove(callbackFunction), backgroundActivity = m_process->throttler().backgroundActivity("WebPageProxy::executeEditCommand"_s)] () mutable { |
| callbackFunction(); |
| }); |
| } |
| |
| void WebPageProxy::executeEditCommand(const String& commandName, const String& argument) |
| { |
| static NeverDestroyed<String> ignoreSpellingCommandName(MAKE_STATIC_STRING_IMPL("ignoreSpelling")); |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| if (auto pasteAccessCategory = pasteAccessCategoryForCommand(commandName)) |
| willPerformPasteCommand(*pasteAccessCategory); |
| |
| if (commandName == ignoreSpellingCommandName) |
| ++m_pendingLearnOrIgnoreWordMessageCount; |
| |
| send(Messages::WebPage::ExecuteEditCommand(commandName, argument)); |
| } |
| |
| void WebPageProxy::requestFontAttributesAtSelectionStart(CompletionHandler<void(const WebCore::FontAttributes&)>&& callback) |
| { |
| if (!hasRunningProcess()) |
| return callback({ }); |
| |
| if (auto attributes = m_cachedFontAttributesAtSelectionStart) { |
| callback(*attributes); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::RequestFontAttributesAtSelectionStart(), [this, protectedThis = Ref { *this }, callback = WTFMove(callback)] (const WebCore::FontAttributes& attributes) mutable { |
| m_cachedFontAttributesAtSelectionStart = attributes; |
| callback(attributes); |
| }); |
| } |
| |
| void WebPageProxy::setEditable(bool editable) |
| { |
| if (editable == m_isEditable) |
| return; |
| |
| m_isEditable = editable; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetEditable(editable)); |
| } |
| |
| void WebPageProxy::setMediaStreamCaptureMuted(bool muted) |
| { |
| auto state = m_mutedState; |
| if (muted) |
| state.add(WebCore::MediaProducer::MediaStreamCaptureIsMuted); |
| else |
| state.remove(WebCore::MediaProducer::MediaStreamCaptureIsMuted); |
| setMuted(state); |
| } |
| |
| #if PLATFORM(MAC) || PLATFORM(MACCATALYST) |
| void WebPageProxy::isConnectedToHardwareConsoleDidChange() |
| { |
| SetForScope<bool> isProcessing(m_isProcessingIsConnectedToHardwareConsoleDidChangeNotification, true); |
| if (m_process->isConnectedToHardwareConsole()) { |
| if (m_captureWasMutedDueToDisconnectedHardwareConsole) |
| setMediaStreamCaptureMuted(false); |
| |
| m_captureWasMutedDueToDisconnectedHardwareConsole = false; |
| return; |
| } |
| |
| if (m_mutedState.containsAny(WebCore::MediaProducer::MediaStreamCaptureIsMuted)) |
| return; |
| |
| m_captureWasMutedDueToDisconnectedHardwareConsole = true; |
| setMediaStreamCaptureMuted(true); |
| } |
| #endif |
| |
| bool WebPageProxy::isAllowedToChangeMuteState() const |
| { |
| #if PLATFORM(MAC) || PLATFORM(MACCATALYST) |
| return m_isProcessingIsConnectedToHardwareConsoleDidChangeNotification || m_process->isConnectedToHardwareConsole(); |
| #else |
| return true; |
| #endif |
| } |
| |
| void WebPageProxy::activateMediaStreamCaptureInPage() |
| { |
| #if ENABLE(MEDIA_STREAM) |
| WebProcessProxy::muteCaptureInPagesExcept(m_webPageID); |
| #endif |
| setMediaStreamCaptureMuted(false); |
| } |
| |
| #if !PLATFORM(IOS_FAMILY) |
| void WebPageProxy::didCommitLayerTree(const RemoteLayerTreeTransaction&) |
| { |
| } |
| |
| void WebPageProxy::layerTreeCommitComplete() |
| { |
| } |
| #endif |
| |
| void WebPageProxy::didUpdateRenderingAfterCommittingLoad() |
| { |
| if (m_hasUpdatedRenderingAfterDidCommitLoad) |
| return; |
| |
| m_hasUpdatedRenderingAfterDidCommitLoad = true; |
| stopMakingViewBlankDueToLackOfRenderingUpdateIfNecessary(); |
| } |
| |
| void WebPageProxy::stopMakingViewBlankDueToLackOfRenderingUpdateIfNecessary() |
| { |
| if (!m_madeViewBlankDueToLackOfRenderingUpdate) |
| return; |
| |
| ASSERT(m_hasUpdatedRenderingAfterDidCommitLoad); |
| WEBPAGEPROXY_RELEASE_LOG(Process, "stopMakingViewBlankDueToLackOfRenderingUpdateIfNecessary:"); |
| pageClient().makeViewBlank(false); |
| m_madeViewBlankDueToLackOfRenderingUpdate = false; |
| } |
| |
| // If we have not painted yet since the last load commit, then we are likely still displaying the previous page. |
| // Displaying a JS prompt for the new page with the old page behind would be confusing so we make the view blank |
| // until the next paint in such case. |
| void WebPageProxy::makeViewBlankIfUnpaintedSinceLastLoadCommit() |
| { |
| if (!m_hasUpdatedRenderingAfterDidCommitLoad) { |
| #if PLATFORM(COCOA) |
| static bool shouldMakeViewBlank = linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::BlanksViewOnJSPrompt); |
| #else |
| static bool shouldMakeViewBlank = true; |
| #endif |
| if (shouldMakeViewBlank) { |
| WEBPAGEPROXY_RELEASE_LOG(Process, "makeViewBlankIfUnpaintedSinceLastLoadCommit: Making the view blank because of a JS prompt before the first paint for its page"); |
| pageClient().makeViewBlank(true); |
| m_madeViewBlankDueToLackOfRenderingUpdate = true; |
| } |
| } |
| } |
| |
| void WebPageProxy::discardQueuedMouseEvents() |
| { |
| while (m_mouseEventQueue.size() > 1) |
| m_mouseEventQueue.removeLast(); |
| } |
| |
| #if ENABLE(DRAG_SUPPORT) |
| void WebPageProxy::dragEntered(DragData& dragData, const String& dragStorageName) |
| { |
| #if PLATFORM(COCOA) |
| WebPasteboardProxy::singleton().grantAccessToCurrentTypes(m_process.get(), dragStorageName); |
| #endif |
| launchInitialProcessIfNecessary(); |
| performDragControllerAction(DragControllerAction::Entered, dragData, dragStorageName, { }, { }); |
| } |
| |
| void WebPageProxy::dragUpdated(DragData& dragData, const String& dragStorageName) |
| { |
| #if PLATFORM(COCOA) |
| WebPasteboardProxy::singleton().grantAccessToCurrentTypes(m_process.get(), dragStorageName); |
| #endif |
| performDragControllerAction(DragControllerAction::Updated, dragData, dragStorageName, { }, { }); |
| } |
| |
| void WebPageProxy::dragExited(DragData& dragData, const String& dragStorageName) |
| { |
| performDragControllerAction(DragControllerAction::Exited, dragData, dragStorageName, { }, { }); |
| } |
| |
| void WebPageProxy::performDragOperation(DragData& dragData, const String& dragStorageName, SandboxExtension::Handle&& sandboxExtensionHandle, Vector<SandboxExtension::Handle>&& sandboxExtensionsForUpload) |
| { |
| #if PLATFORM(COCOA) |
| grantAccessToCurrentPasteboardData(dragStorageName); |
| #endif |
| performDragControllerAction(DragControllerAction::PerformDragOperation, dragData, dragStorageName, WTFMove(sandboxExtensionHandle), WTFMove(sandboxExtensionsForUpload)); |
| } |
| |
| void WebPageProxy::performDragControllerAction(DragControllerAction action, DragData& dragData, const String& dragStorageName, SandboxExtension::Handle&& sandboxExtensionHandle, Vector<SandboxExtension::Handle>&& sandboxExtensionsForUpload) |
| { |
| if (!hasRunningProcess()) |
| return; |
| #if PLATFORM(GTK) |
| UNUSED_PARAM(dragStorageName); |
| UNUSED_PARAM(sandboxExtensionHandle); |
| UNUSED_PARAM(sandboxExtensionsForUpload); |
| |
| String url = dragData.asURL(); |
| if (!url.isEmpty()) |
| m_process->assumeReadAccessToBaseURL(*this, url); |
| |
| ASSERT(dragData.platformData()); |
| send(Messages::WebPage::PerformDragControllerAction(action, dragData.clientPosition(), dragData.globalPosition(), dragData.draggingSourceOperationMask(), *dragData.platformData(), dragData.flags())); |
| #else |
| send(Messages::WebPage::PerformDragControllerAction(action, dragData, sandboxExtensionHandle, sandboxExtensionsForUpload)); |
| #endif |
| } |
| |
| void WebPageProxy::didPerformDragControllerAction(std::optional<WebCore::DragOperation> dragOperation, WebCore::DragHandlingMethod dragHandlingMethod, bool mouseIsOverFileInput, unsigned numberOfItemsToBeAccepted, const IntRect& insertionRect, const IntRect& editableElementRect) |
| { |
| m_currentDragOperation = dragOperation; |
| m_currentDragHandlingMethod = dragHandlingMethod; |
| m_currentDragIsOverFileInput = mouseIsOverFileInput; |
| m_currentDragNumberOfFilesToBeAccepted = numberOfItemsToBeAccepted; |
| m_currentDragCaretEditableElementRect = editableElementRect; |
| setDragCaretRect(insertionRect); |
| pageClient().didPerformDragControllerAction(); |
| } |
| |
| #if PLATFORM(GTK) |
| void WebPageProxy::startDrag(SelectionData&& selectionData, OptionSet<WebCore::DragOperation> dragOperationMask, const ShareableBitmap::Handle& dragImageHandle, IntPoint&& dragImageHotspot) |
| { |
| RefPtr<ShareableBitmap> dragImage = !dragImageHandle.isNull() ? ShareableBitmap::create(dragImageHandle) : nullptr; |
| pageClient().startDrag(WTFMove(selectionData), dragOperationMask, WTFMove(dragImage), WTFMove(dragImageHotspot)); |
| |
| didStartDrag(); |
| } |
| #endif |
| |
| void WebPageProxy::dragEnded(const IntPoint& clientPosition, const IntPoint& globalPosition, OptionSet<WebCore::DragOperation> dragOperationMask) |
| { |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::DragEnded(clientPosition, globalPosition, dragOperationMask)); |
| setDragCaretRect({ }); |
| } |
| |
| void WebPageProxy::didPerformDragOperation(bool handled) |
| { |
| pageClient().didPerformDragOperation(handled); |
| } |
| |
| void WebPageProxy::didStartDrag() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| discardQueuedMouseEvents(); |
| send(Messages::WebPage::DidStartDrag()); |
| } |
| |
| void WebPageProxy::dragCancelled() |
| { |
| if (hasRunningProcess()) |
| send(Messages::WebPage::DragCancelled()); |
| } |
| |
| void WebPageProxy::didEndDragging() |
| { |
| resetCurrentDragInformation(); |
| } |
| |
| void WebPageProxy::resetCurrentDragInformation() |
| { |
| m_currentDragOperation = std::nullopt; |
| m_currentDragHandlingMethod = DragHandlingMethod::None; |
| m_currentDragIsOverFileInput = false; |
| m_currentDragNumberOfFilesToBeAccepted = 0; |
| setDragCaretRect({ }); |
| } |
| |
| #if !PLATFORM(IOS_FAMILY) || !ENABLE(DRAG_SUPPORT) |
| |
| void WebPageProxy::setDragCaretRect(const IntRect& dragCaretRect) |
| { |
| m_currentDragCaretRect = dragCaretRect; |
| } |
| |
| #endif |
| |
| #endif // ENABLE(DRAG_SUPPORT) |
| |
| static bool removeOldRedundantEvent(Deque<NativeWebMouseEvent>& queue, WebEvent::Type incomingEventType) |
| { |
| if (incomingEventType != WebEvent::MouseMove && incomingEventType != WebEvent::MouseForceChanged) |
| return false; |
| |
| auto it = queue.rbegin(); |
| auto end = queue.rend(); |
| |
| // Must not remove the first event in the deque, since it is already being dispatched. |
| if (it != end) |
| --end; |
| |
| for (; it != end; ++it) { |
| auto type = it->type(); |
| if (type == incomingEventType) { |
| queue.remove(--it.base()); |
| return true; |
| } |
| if (type != WebEvent::MouseMove && type != WebEvent::MouseForceChanged) |
| break; |
| } |
| return false; |
| } |
| |
| void WebPageProxy::handleMouseEvent(const NativeWebMouseEvent& event) |
| { |
| if (event.type() == WebEvent::MouseDown) |
| launchInitialProcessIfNecessary(); |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| #if ENABLE(ASYNC_SCROLLING) && PLATFORM(COCOA) |
| if (m_scrollingCoordinatorProxy) |
| m_scrollingCoordinatorProxy->handleMouseEvent(platform(event)); |
| #endif |
| |
| // If we receive multiple mousemove or mouseforcechanged events and the most recent mousemove or mouseforcechanged event |
| // (respectively) has not yet been sent to WebProcess for processing, remove the pending mouse event and insert the new |
| // event in the queue. |
| bool didRemoveEvent = removeOldRedundantEvent(m_mouseEventQueue, event.type()); |
| m_mouseEventQueue.append(event); |
| |
| #if LOG_DISABLED |
| UNUSED_PARAM(didRemoveEvent); |
| #else |
| LOG(MouseHandling, "UIProcess: %s mouse event %s (queue size %zu)", didRemoveEvent ? "replaced" : "enqueued", webMouseEventTypeString(event.type()), m_mouseEventQueue.size()); |
| #endif |
| |
| if (m_mouseEventQueue.size() == 1) // Otherwise, called from DidReceiveEvent message handler. |
| processNextQueuedMouseEvent(); |
| } |
| |
| void WebPageProxy::processNextQueuedMouseEvent() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| ASSERT(!m_mouseEventQueue.isEmpty()); |
| |
| const NativeWebMouseEvent& event = m_mouseEventQueue.first(); |
| |
| if (pageClient().windowIsFrontWindowUnderMouse(event)) |
| setToolTip(String()); |
| |
| WebEvent::Type eventType = event.type(); |
| if (eventType == WebEvent::MouseDown || eventType == WebEvent::MouseForceChanged || eventType == WebEvent::MouseForceDown) |
| m_process->startResponsivenessTimer(WebProcessProxy::UseLazyStop::Yes); |
| else if (eventType != WebEvent::MouseMove) { |
| // NOTE: This does not start the responsiveness timer because mouse move should not indicate interaction. |
| m_process->startResponsivenessTimer(); |
| } |
| |
| std::optional<Vector<SandboxExtension::Handle>> sandboxExtensions; |
| |
| #if PLATFORM(MAC) |
| bool eventMayStartDrag = !m_currentDragOperation && eventType == WebEvent::MouseMove && event.button() != WebMouseEvent::Button::NoButton; |
| if (eventMayStartDrag) |
| sandboxExtensions = SandboxExtension::createHandlesForMachLookup({ "com.apple.iconservices"_s, "com.apple.iconservices.store"_s }, process().auditToken(), SandboxExtension::MachBootstrapOptions::EnableMachBootstrap); |
| #endif |
| |
| LOG(MouseHandling, "UIProcess: sent mouse event %s (queue size %zu)", webMouseEventTypeString(eventType), m_mouseEventQueue.size()); |
| send(Messages::WebPage::MouseEvent(event, sandboxExtensions)); |
| } |
| |
| void WebPageProxy::doAfterProcessingAllPendingMouseEvents(WTF::Function<void ()>&& action) |
| { |
| if (!isProcessingMouseEvents()) { |
| action(); |
| return; |
| } |
| |
| m_callbackHandlersAfterProcessingPendingMouseEvents.append(WTFMove(action)); |
| } |
| |
| void WebPageProxy::didFinishProcessingAllPendingMouseEvents() |
| { |
| flushPendingMouseEventCallbacks(); |
| } |
| |
| void WebPageProxy::flushPendingMouseEventCallbacks() |
| { |
| for (auto& callback : m_callbackHandlersAfterProcessingPendingMouseEvents) |
| callback(); |
| |
| m_callbackHandlersAfterProcessingPendingMouseEvents.clear(); |
| } |
| |
| void WebPageProxy::dispatchWheelEventWithoutScrolling(const WebWheelEvent& event, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| sendWithAsyncReply(Messages::WebPage::DispatchWheelEventWithoutScrolling(event), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::handleWheelEvent(const NativeWebWheelEvent& event) |
| { |
| #if ENABLE(ASYNC_SCROLLING) && PLATFORM(COCOA) |
| if (m_scrollingCoordinatorProxy && m_scrollingCoordinatorProxy->handleWheelEvent(platform(event))) |
| return; |
| #endif |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| closeOverlayedViews(); |
| |
| #if ENABLE(MOMENTUM_EVENT_DISPATCHER) |
| // FIXME: We should not have to look this up repeatedly, but it can also change occasionally. |
| if (event.momentumPhase() == WebWheelEvent::PhaseBegan && preferences().momentumScrollingAnimatorEnabled()) |
| m_scrollingAccelerationCurve = ScrollingAccelerationCurve::fromNativeWheelEvent(event); |
| #endif |
| |
| if (wheelEventCoalescer().shouldDispatchEvent(event)) { |
| auto event = wheelEventCoalescer().nextEventToDispatch(); |
| sendWheelEvent(*event); |
| } |
| } |
| |
| #if HAVE(CVDISPLAYLINK) |
| void WebPageProxy::wheelEventHysteresisUpdated(PAL::HysteresisState) |
| { |
| updateDisplayLinkFrequency(); |
| } |
| |
| void WebPageProxy::updateDisplayLinkFrequency() |
| { |
| if (!m_process->hasConnection() || !m_displayID) |
| return; |
| |
| bool wantsFullSpeedUpdates = m_hasActiveAnimatedScroll || m_wheelEventActivityHysteresis.state() == PAL::HysteresisState::Started; |
| if (wantsFullSpeedUpdates != m_registeredForFullSpeedUpdates) { |
| process().processPool().setDisplayLinkForDisplayWantsFullSpeedUpdates(*m_process->connection(), *m_displayID, wantsFullSpeedUpdates); |
| m_registeredForFullSpeedUpdates = wantsFullSpeedUpdates; |
| } |
| } |
| #endif |
| |
| void WebPageProxy::updateWheelEventActivityAfterProcessSwap() |
| { |
| #if HAVE(CVDISPLAYLINK) |
| updateDisplayLinkFrequency(); |
| #endif |
| } |
| |
| void WebPageProxy::sendWheelEvent(const WebWheelEvent& event) |
| { |
| #if HAVE(CVDISPLAYLINK) |
| m_wheelEventActivityHysteresis.impulse(); |
| #endif |
| |
| auto* connection = messageSenderConnection(); |
| if (!connection) |
| return; |
| |
| auto rubberBandableEdges = this->rubberBandableEdges(); |
| if (shouldUseImplicitRubberBandControl()) { |
| rubberBandableEdges.setLeft(!m_backForwardList->backItem()); |
| rubberBandableEdges.setRight(!m_backForwardList->forwardItem()); |
| } |
| |
| #if ENABLE(MOMENTUM_EVENT_DISPATCHER) |
| if (event.momentumPhase() == WebWheelEvent::PhaseBegan && m_scrollingAccelerationCurve != m_lastSentScrollingAccelerationCurve) { |
| connection->send(Messages::EventDispatcher::SetScrollingAccelerationCurve(m_webPageID, m_scrollingAccelerationCurve), 0, { }, Thread::QOS::UserInteractive); |
| m_lastSentScrollingAccelerationCurve = m_scrollingAccelerationCurve; |
| } |
| #endif |
| |
| connection->send(Messages::EventDispatcher::WheelEvent(m_webPageID, event, rubberBandableEdges), 0, { }, Thread::QOS::UserInteractive); |
| |
| // Manually ping the web process to check for responsiveness since our wheel |
| // event will dispatch to a non-main thread, which always responds. |
| m_process->isResponsiveWithLazyStop(); |
| } |
| |
| WebWheelEventCoalescer& WebPageProxy::wheelEventCoalescer() |
| { |
| if (!m_wheelEventCoalescer) |
| m_wheelEventCoalescer = makeUnique<WebWheelEventCoalescer>(); |
| |
| return *m_wheelEventCoalescer; |
| } |
| |
| bool WebPageProxy::hasQueuedKeyEvent() const |
| { |
| return !m_keyEventQueue.isEmpty(); |
| } |
| |
| const NativeWebKeyboardEvent& WebPageProxy::firstQueuedKeyEvent() const |
| { |
| return m_keyEventQueue.first(); |
| } |
| |
| bool WebPageProxy::handleKeyboardEvent(const NativeWebKeyboardEvent& event) |
| { |
| if (!hasRunningProcess()) |
| return false; |
| |
| LOG(KeyHandling, "WebPageProxy::handleKeyboardEvent: %s", webKeyboardEventTypeString(event.type())); |
| |
| m_keyEventQueue.append(event); |
| |
| m_process->startResponsivenessTimer(event.type() == WebEvent::KeyDown ? WebProcessProxy::UseLazyStop::Yes : WebProcessProxy::UseLazyStop::No); |
| |
| if (m_keyEventQueue.size() == 1) { // Otherwise, sent from DidReceiveEvent message handler. |
| LOG(KeyHandling, " UI process: sent keyEvent from handleKeyboardEvent"); |
| send(Messages::WebPage::KeyEvent(event)); |
| } |
| |
| return true; |
| } |
| |
| WebPreferencesStore WebPageProxy::preferencesStore() const |
| { |
| return m_preferences->store(); |
| } |
| |
| #if ENABLE(TOUCH_EVENTS) |
| |
| static TrackingType mergeTrackingTypes(TrackingType a, TrackingType b) |
| { |
| if (static_cast<uintptr_t>(b) > static_cast<uintptr_t>(a)) |
| return b; |
| return a; |
| } |
| |
| void WebPageProxy::updateTouchEventTracking(const WebTouchEvent& touchStartEvent) |
| { |
| #if ENABLE(ASYNC_SCROLLING) && PLATFORM(COCOA) |
| for (auto& touchPoint : touchStartEvent.touchPoints()) { |
| IntPoint location = touchPoint.location(); |
| auto updateTrackingType = [this, location](TrackingType& trackingType, EventTrackingRegions::EventType eventType) { |
| if (trackingType == TrackingType::Synchronous) |
| return; |
| |
| TrackingType trackingTypeForLocation = m_scrollingCoordinatorProxy->eventTrackingTypeForPoint(eventType, location); |
| |
| trackingType = mergeTrackingTypes(trackingType, trackingTypeForLocation); |
| }; |
| updateTrackingType(m_touchAndPointerEventTracking.touchForceChangedTracking, EventTrackingRegions::EventType::Touchforcechange); |
| updateTrackingType(m_touchAndPointerEventTracking.touchStartTracking, EventTrackingRegions::EventType::Touchstart); |
| updateTrackingType(m_touchAndPointerEventTracking.touchMoveTracking, EventTrackingRegions::EventType::Touchmove); |
| updateTrackingType(m_touchAndPointerEventTracking.touchEndTracking, EventTrackingRegions::EventType::Touchend); |
| updateTrackingType(m_touchAndPointerEventTracking.touchStartTracking, EventTrackingRegions::EventType::Pointerover); |
| updateTrackingType(m_touchAndPointerEventTracking.touchStartTracking, EventTrackingRegions::EventType::Pointerenter); |
| updateTrackingType(m_touchAndPointerEventTracking.touchStartTracking, EventTrackingRegions::EventType::Pointerdown); |
| updateTrackingType(m_touchAndPointerEventTracking.touchMoveTracking, EventTrackingRegions::EventType::Pointermove); |
| updateTrackingType(m_touchAndPointerEventTracking.touchEndTracking, EventTrackingRegions::EventType::Pointerup); |
| updateTrackingType(m_touchAndPointerEventTracking.touchEndTracking, EventTrackingRegions::EventType::Pointerout); |
| updateTrackingType(m_touchAndPointerEventTracking.touchEndTracking, EventTrackingRegions::EventType::Pointerleave); |
| updateTrackingType(m_touchAndPointerEventTracking.touchStartTracking, EventTrackingRegions::EventType::Mousedown); |
| updateTrackingType(m_touchAndPointerEventTracking.touchMoveTracking, EventTrackingRegions::EventType::Mousemove); |
| updateTrackingType(m_touchAndPointerEventTracking.touchEndTracking, EventTrackingRegions::EventType::Mouseup); |
| } |
| #else |
| UNUSED_PARAM(touchStartEvent); |
| m_touchAndPointerEventTracking.touchForceChangedTracking = TrackingType::Synchronous; |
| m_touchAndPointerEventTracking.touchStartTracking = TrackingType::Synchronous; |
| m_touchAndPointerEventTracking.touchMoveTracking = TrackingType::Synchronous; |
| m_touchAndPointerEventTracking.touchEndTracking = TrackingType::Synchronous; |
| #endif // ENABLE(ASYNC_SCROLLING) |
| } |
| |
| TrackingType WebPageProxy::touchEventTrackingType(const WebTouchEvent& touchStartEvent) const |
| { |
| // We send all events if any type is needed, we just do it asynchronously for the types that are not tracked. |
| // |
| // Touch events define a sequence with strong dependencies. For example, we can expect |
| // a TouchMove to only appear after a TouchStart, and the ids of the touch points is consistent between |
| // the two. |
| // |
| // WebCore should not have to set up its state correctly after some events were dismissed. |
| // For example, we don't want to send a TouchMoved without a TouchPressed. |
| // We send everything, WebCore updates its internal state and dispatch what is needed to the page. |
| TrackingType globalTrackingType = m_touchAndPointerEventTracking.isTrackingAnything() ? TrackingType::Asynchronous : TrackingType::NotTracking; |
| |
| globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchAndPointerEventTracking.touchForceChangedTracking); |
| for (auto& touchPoint : touchStartEvent.touchPoints()) { |
| switch (touchPoint.state()) { |
| case WebPlatformTouchPoint::TouchReleased: |
| globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchAndPointerEventTracking.touchEndTracking); |
| break; |
| case WebPlatformTouchPoint::TouchPressed: |
| globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchAndPointerEventTracking.touchStartTracking); |
| break; |
| case WebPlatformTouchPoint::TouchMoved: |
| case WebPlatformTouchPoint::TouchStationary: |
| globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchAndPointerEventTracking.touchMoveTracking); |
| break; |
| case WebPlatformTouchPoint::TouchCancelled: |
| globalTrackingType = mergeTrackingTypes(globalTrackingType, TrackingType::Asynchronous); |
| break; |
| } |
| } |
| |
| return globalTrackingType; |
| } |
| |
| #endif |
| |
| #if ENABLE(MAC_GESTURE_EVENTS) |
| void WebPageProxy::handleGestureEvent(const NativeWebGestureEvent& event) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| m_gestureEventQueue.append(event); |
| // FIXME: Consider doing some coalescing here. |
| |
| m_process->startResponsivenessTimer((event.type() == WebEvent::GestureStart || event.type() == WebEvent::GestureChange) ? WebProcessProxy::UseLazyStop::Yes : WebProcessProxy::UseLazyStop::No); |
| |
| send(Messages::EventDispatcher::GestureEvent(m_webPageID, event), 0); |
| } |
| #endif |
| |
| #if ENABLE(IOS_TOUCH_EVENTS) |
| void WebPageProxy::handlePreventableTouchEvent(NativeWebTouchEvent& event) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| TraceScope scope(SyncTouchEventStart, SyncTouchEventEnd); |
| |
| updateTouchEventTracking(event); |
| |
| auto handleAllTouchPointsReleased = WTF::makeScopeExit([&] { |
| if (!event.allTouchPointsAreReleased()) |
| return; |
| |
| m_touchAndPointerEventTracking.reset(); |
| didReleaseAllTouchPoints(); |
| }); |
| |
| bool isTouchStart = event.type() == WebEvent::TouchStart; |
| bool isTouchMove = event.type() == WebEvent::TouchMove; |
| bool isTouchEnd = event.type() == WebEvent::TouchEnd; |
| |
| if (isTouchStart) |
| m_touchMovePreventionState = TouchMovePreventionState::NotWaiting; |
| |
| TrackingType touchEventsTrackingType = touchEventTrackingType(event); |
| if (touchEventsTrackingType == TrackingType::NotTracking) { |
| if (isTouchStart) |
| pageClient().doneDeferringTouchStart(false); |
| if (isTouchMove) |
| pageClient().doneDeferringTouchMove(false); |
| if (isTouchEnd) |
| pageClient().doneDeferringTouchEnd(false); |
| return; |
| } |
| |
| if (touchEventsTrackingType == TrackingType::Asynchronous) { |
| // We can end up here if a native gesture has not started but the event handlers are passive. |
| // |
| // The client of WebPageProxy asks the event to be sent synchronously since the touch event |
| // can prevent a native gesture. |
| // But, here we know that all events handlers that can handle this events are passive. |
| // We can use asynchronous dispatch and pretend to the client that the page does nothing with the events. |
| event.setCanPreventNativeGestures(false); |
| handleUnpreventableTouchEvent(event); |
| didReceiveEvent(event.type(), false); |
| if (isTouchStart) |
| pageClient().doneDeferringTouchStart(false); |
| if (isTouchMove) |
| pageClient().doneDeferringTouchMove(false); |
| if (isTouchEnd) |
| pageClient().doneDeferringTouchEnd(false); |
| return; |
| } |
| |
| if (isTouchStart) |
| ++m_handlingPreventableTouchStartCount; |
| |
| if (isTouchMove && m_touchMovePreventionState == TouchMovePreventionState::NotWaiting) |
| m_touchMovePreventionState = TouchMovePreventionState::Waiting; |
| |
| if (isTouchEnd) |
| ++m_handlingPreventableTouchEndCount; |
| |
| sendWithAsyncReply(Messages::EventDispatcher::TouchEvent(m_webPageID, event), [this, weakThis = WeakPtr { *this }, event] (bool handled) { |
| RefPtr protectedThis = weakThis.get(); |
| if (!protectedThis) |
| return; |
| |
| bool didFinishDeferringTouchStart = false; |
| ASSERT_IMPLIES(event.type() == WebEvent::TouchStart, m_handlingPreventableTouchStartCount); |
| if (event.type() == WebEvent::TouchStart && m_handlingPreventableTouchStartCount) |
| didFinishDeferringTouchStart = !--m_handlingPreventableTouchStartCount; |
| |
| bool didFinishDeferringTouchMove = false; |
| if (event.type() == WebEvent::TouchMove && m_touchMovePreventionState == TouchMovePreventionState::Waiting) { |
| m_touchMovePreventionState = TouchMovePreventionState::ReceivedReply; |
| didFinishDeferringTouchMove = true; |
| } |
| |
| bool didFinishDeferringTouchEnd = false; |
| ASSERT_IMPLIES(event.type() == WebEvent::TouchEnd, m_handlingPreventableTouchEndCount); |
| if (event.type() == WebEvent::TouchEnd && m_handlingPreventableTouchEndCount) |
| didFinishDeferringTouchEnd = !--m_handlingPreventableTouchEndCount; |
| |
| didReceiveEvent(event.type(), handled); |
| if (!m_pageClient) |
| return; |
| |
| pageClient().doneWithTouchEvent(event, handled); |
| |
| if (didFinishDeferringTouchStart) |
| pageClient().doneDeferringTouchStart(handled); |
| |
| if (didFinishDeferringTouchMove) |
| pageClient().doneDeferringTouchMove(handled); |
| |
| if (didFinishDeferringTouchEnd) |
| pageClient().doneDeferringTouchEnd(handled); |
| }); |
| } |
| |
| void WebPageProxy::resetPotentialTapSecurityOrigin() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ResetPotentialTapSecurityOrigin()); |
| } |
| |
| void WebPageProxy::handleUnpreventableTouchEvent(const NativeWebTouchEvent& event) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| TrackingType touchEventsTrackingType = touchEventTrackingType(event); |
| if (touchEventsTrackingType == TrackingType::NotTracking) |
| return; |
| |
| send(Messages::EventDispatcher::TouchEventWithoutCallback(m_webPageID, event), 0); |
| |
| if (event.allTouchPointsAreReleased()) { |
| m_touchAndPointerEventTracking.reset(); |
| didReleaseAllTouchPoints(); |
| } |
| } |
| |
| #elif ENABLE(TOUCH_EVENTS) |
| void WebPageProxy::handleTouchEvent(const NativeWebTouchEvent& event) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| updateTouchEventTracking(event); |
| |
| if (touchEventTrackingType(event) == TrackingType::NotTracking) |
| return; |
| |
| // If the page is suspended, which should be the case during panning, pinching |
| // and animation on the page itself (kinetic scrolling, tap to zoom) etc, then |
| // we do not send any of the events to the page even if is has listeners. |
| if (!m_areActiveDOMObjectsAndAnimationsSuspended) { |
| m_touchEventQueue.append(event); |
| m_process->startResponsivenessTimer(); |
| send(Messages::WebPage::TouchEvent(event)); |
| } else { |
| if (m_touchEventQueue.isEmpty()) { |
| bool isEventHandled = false; |
| pageClient().doneWithTouchEvent(event, isEventHandled); |
| } else { |
| // We attach the incoming events to the newest queued event so that all |
| // the events are delivered in the correct order when the event is dequed. |
| QueuedTouchEvents& lastEvent = m_touchEventQueue.last(); |
| lastEvent.deferredTouchEvents.append(event); |
| } |
| } |
| |
| if (event.allTouchPointsAreReleased()) { |
| m_touchAndPointerEventTracking.reset(); |
| didReleaseAllTouchPoints(); |
| } |
| } |
| #endif // ENABLE(TOUCH_EVENTS) |
| |
| void WebPageProxy::cancelPointer(WebCore::PointerID pointerId, const WebCore::IntPoint& documentPoint) |
| { |
| send(Messages::WebPage::CancelPointer(pointerId, documentPoint)); |
| } |
| |
| void WebPageProxy::touchWithIdentifierWasRemoved(WebCore::PointerID pointerId) |
| { |
| send(Messages::WebPage::TouchWithIdentifierWasRemoved(pointerId)); |
| } |
| |
| void WebPageProxy::scrollBy(ScrollDirection direction, ScrollGranularity granularity) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ScrollBy(direction, granularity)); |
| } |
| |
| void WebPageProxy::centerSelectionInVisibleArea() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::CenterSelectionInVisibleArea()); |
| } |
| |
| class WebPageProxy::PolicyDecisionSender : public RefCounted<PolicyDecisionSender> { |
| public: |
| using SendFunction = CompletionHandler<void(PolicyDecision&&)>; |
| |
| static Ref<PolicyDecisionSender> create(PolicyCheckIdentifier identifier, SendFunction&& sendFunction) |
| { |
| return adoptRef(*new PolicyDecisionSender(identifier, WTFMove(sendFunction))); |
| } |
| |
| void send(PolicyDecision&& policyDecision) |
| { |
| if (m_sendFunction) |
| m_sendFunction(WTFMove(policyDecision)); |
| } |
| |
| PolicyCheckIdentifier identifier() { return m_identifier; } |
| |
| private: |
| PolicyDecisionSender(PolicyCheckIdentifier identifier, SendFunction sendFunction) |
| : m_sendFunction(WTFMove(sendFunction)) |
| , m_identifier(identifier) |
| { } |
| |
| SendFunction m_sendFunction; |
| PolicyCheckIdentifier m_identifier; |
| }; |
| |
| #if ENABLE(APP_BOUND_DOMAINS) |
| static bool shouldTreatURLProtocolAsAppBound(const URL& requestURL, bool isRunningTest) |
| { |
| return !isRunningTest |
| && (SecurityOrigin::isLocalHostOrLoopbackIPAddress(requestURL.host()) |
| || requestURL.protocolIsAbout() |
| || requestURL.protocolIsData() |
| || requestURL.protocolIsBlob() |
| || requestURL.isLocalFile() |
| || requestURL.protocolIsJavaScript()); |
| } |
| |
| bool WebPageProxy::setIsNavigatingToAppBoundDomainAndCheckIfPermitted(bool isMainFrame, const URL& requestURL, std::optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain) |
| { |
| if (isFullWebBrowser()) { |
| if (hasProhibitedUsageStrings()) |
| m_isNavigatingToAppBoundDomain = NavigatingToAppBoundDomain::No; |
| return true; |
| } |
| |
| if (!isNavigatingToAppBoundDomain) { |
| m_isNavigatingToAppBoundDomain = std::nullopt; |
| return true; |
| } |
| if (m_ignoresAppBoundDomains) |
| return true; |
| |
| if (isMainFrame && shouldTreatURLProtocolAsAppBound(requestURL, websiteDataStore().configuration().enableInAppBrowserPrivacyForTesting())) { |
| isNavigatingToAppBoundDomain = NavigatingToAppBoundDomain::Yes; |
| m_limitsNavigationsToAppBoundDomains = true; |
| } |
| if (m_limitsNavigationsToAppBoundDomains) { |
| if (*isNavigatingToAppBoundDomain == NavigatingToAppBoundDomain::No) { |
| if (isMainFrame) |
| return false; |
| m_isNavigatingToAppBoundDomain = NavigatingToAppBoundDomain::No; |
| return true; |
| } |
| m_isNavigatingToAppBoundDomain = NavigatingToAppBoundDomain::Yes; |
| } else { |
| if (m_hasExecutedAppBoundBehaviorBeforeNavigation) |
| return false; |
| m_isNavigatingToAppBoundDomain = NavigatingToAppBoundDomain::No; |
| } |
| return true; |
| } |
| |
| void WebPageProxy::isNavigatingToAppBoundDomainTesting(CompletionHandler<void(bool)>&& completionHandler) |
| { |
| completionHandler(m_isNavigatingToAppBoundDomain && (*m_isNavigatingToAppBoundDomain == NavigatingToAppBoundDomain::Yes)); |
| } |
| |
| void WebPageProxy::isForcedIntoAppBoundModeTesting(CompletionHandler<void(bool)>&& completionHandler) |
| { |
| completionHandler(m_limitsNavigationsToAppBoundDomains); |
| } |
| #endif |
| |
| void WebPageProxy::disableServiceWorkerEntitlementInNetworkProcess() |
| { |
| #if ENABLE(APP_BOUND_DOMAINS) && !PLATFORM(MACCATALYST) |
| websiteDataStore().networkProcess().send(Messages::NetworkProcess::DisableServiceWorkerEntitlement(), 0); |
| #endif |
| } |
| |
| void WebPageProxy::clearServiceWorkerEntitlementOverride(CompletionHandler<void()>&& completionHandler) |
| { |
| #if ENABLE(APP_BOUND_DOMAINS) && !PLATFORM(MACCATALYST) |
| auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler)); |
| sendWithAsyncReply(Messages::WebPage::ClearServiceWorkerEntitlementOverride(), [callbackAggregator] { }); |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::ClearServiceWorkerEntitlementOverride(), [callbackAggregator] { }); |
| #else |
| completionHandler(); |
| #endif |
| } |
| |
| void WebPageProxy::receivedNavigationPolicyDecision(PolicyAction policyAction, API::Navigation* navigation, Ref<API::NavigationAction>&& navigationAction, ProcessSwapRequestedByClient processSwapRequestedByClient, WebFrameProxy& frame, const FrameInfoData& frameInfo, Ref<PolicyDecisionSender>&& sender) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "receivedNavigationPolicyDecision: frameID=%llu, isMainFrame=%d, navigationID=%llu, policyAction=%u", frame.frameID().toUInt64(), frame.isMainFrame(), navigation ? navigation->navigationID() : 0, (unsigned)policyAction); |
| |
| Ref<WebsiteDataStore> websiteDataStore = m_websiteDataStore.copyRef(); |
| if (auto* policies = navigation->websitePolicies()) { |
| if (policies->websiteDataStore() && policies->websiteDataStore() != websiteDataStore.ptr()) { |
| websiteDataStore = *policies->websiteDataStore(); |
| processSwapRequestedByClient = ProcessSwapRequestedByClient::Yes; |
| } |
| if (policies->userContentController() && policies->userContentController() != m_userContentController.ptr()) |
| processSwapRequestedByClient = ProcessSwapRequestedByClient::Yes; |
| } |
| |
| if (navigation && !navigation->userContentExtensionsEnabled()) { |
| if (!navigation->websitePolicies()) |
| navigation->setWebsitePolicies(API::WebsitePolicies::create()); |
| navigation->websitePolicies()->setContentBlockersEnabled(false); |
| } |
| |
| #if ENABLE(DEVICE_ORIENTATION) |
| if (navigation && (!navigation->websitePolicies() || navigation->websitePolicies()->deviceOrientationAndMotionAccessState() == WebCore::DeviceOrientationOrMotionPermissionState::Prompt)) { |
| auto deviceOrientationPermission = websiteDataStore->deviceOrientationAndMotionAccessController().cachedDeviceOrientationPermission(SecurityOriginData::fromURL(navigation->currentRequest().url())); |
| if (deviceOrientationPermission != WebCore::DeviceOrientationOrMotionPermissionState::Prompt) { |
| if (!navigation->websitePolicies()) |
| navigation->setWebsitePolicies(API::WebsitePolicies::create()); |
| navigation->websitePolicies()->setDeviceOrientationAndMotionAccessState(deviceOrientationPermission); |
| } |
| } |
| #endif |
| |
| #if PLATFORM(COCOA) |
| static const bool forceDownloadFromDownloadAttribute = false; |
| #else |
| static const bool forceDownloadFromDownloadAttribute = true; |
| #endif |
| if (policyAction == PolicyAction::Use && navigation && (navigation->isSystemPreview() || (forceDownloadFromDownloadAttribute && navigation->shouldPerformDownload()))) |
| policyAction = PolicyAction::Download; |
| |
| if (policyAction != PolicyAction::Use || !frame.isMainFrame() || !navigation) { |
| receivedPolicyDecision(policyAction, navigation, navigation->websitePolicies(), WTFMove(navigationAction), WTFMove(sender)); |
| return; |
| } |
| |
| Ref<WebProcessProxy> sourceProcess = process(); |
| URL sourceURL { pageLoadState().url() }; |
| if (auto* provisionalPage = provisionalPageProxy()) { |
| if (provisionalPage->navigationID() == navigation->navigationID()) { |
| sourceProcess = provisionalPage->process(); |
| sourceURL = provisionalPage->provisionalURL(); |
| } |
| } |
| |
| m_isCaptivePortalModeExplicitlySet = (navigation->websitePolicies() && navigation->websitePolicies()->isCaptivePortalModeExplicitlySet()) || m_configuration->isCaptivePortalModeExplicitlySet(); |
| auto captivePortalMode = (navigation->websitePolicies() ? navigation->websitePolicies()->captivePortalModeEnabled() : shouldEnableCaptivePortalMode()) ? WebProcessProxy::CaptivePortalMode::Enabled : WebProcessProxy::CaptivePortalMode::Disabled; |
| process().processPool().processForNavigation(*this, *navigation, sourceProcess.copyRef(), sourceURL, processSwapRequestedByClient, captivePortalMode, frameInfo, WTFMove(websiteDataStore), [this, protectedThis = Ref { *this }, policyAction, navigation = Ref { *navigation }, navigationAction = WTFMove(navigationAction), sourceProcess = sourceProcess.copyRef(), sender = WTFMove(sender), processSwapRequestedByClient] (Ref<WebProcessProxy>&& processForNavigation, SuspendedPageProxy* destinationSuspendedPage, const String& reason) mutable { |
| // If the navigation has been destroyed, then no need to proceed. |
| if (isClosed() || !navigationState().hasNavigation(navigation->navigationID())) { |
| receivedPolicyDecision(policyAction, navigation.ptr(), navigation->websitePolicies(), WTFMove(navigationAction), WTFMove(sender)); |
| return; |
| } |
| |
| bool shouldProcessSwap = processForNavigation.ptr() != sourceProcess.ptr(); |
| if (shouldProcessSwap) { |
| policyAction = PolicyAction::StopAllLoads; |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "decidePolicyForNavigationAction, swapping process %i with process %i for navigation, reason=%" PUBLIC_LOG_STRING, processIdentifier(), processForNavigation->processIdentifier(), reason.utf8().data()); |
| LOG(ProcessSwapping, "(ProcessSwapping) Switching from process %i to new process (%i) for navigation %" PRIu64 " '%s'", processIdentifier(), processForNavigation->processIdentifier(), navigation->navigationID(), navigation->loggingString()); |
| } else |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "decidePolicyForNavigationAction: keep using process %i for navigation, reason=%" PUBLIC_LOG_STRING, processIdentifier(), reason.utf8().data()); |
| |
| if (shouldProcessSwap) { |
| // Make sure the process to be used for the navigation does not get shutDown now due to destroying SuspendedPageProxy or ProvisionalPageProxy objects. |
| auto preventNavigationProcessShutdown = processForNavigation->shutdownPreventingScope(); |
| |
| ASSERT(!destinationSuspendedPage || navigation->targetItem()); |
| auto suspendedPage = destinationSuspendedPage ? backForwardCache().takeSuspendedPage(*navigation->targetItem()) : nullptr; |
| |
| // It is difficult to get history right if we have several WebPage objects inside a single WebProcess for the same WebPageProxy. As a result, if we make sure to |
| // clear any SuspendedPageProxy for the current page that are backed by the destination process before we proceed with the navigation. This makes sure the WebPage |
| // we are about to create in the destination process will be the only one associated with this WebPageProxy. |
| if (!destinationSuspendedPage) |
| backForwardCache().removeEntriesForPageAndProcess(*this, processForNavigation); |
| |
| ASSERT(suspendedPage.get() == destinationSuspendedPage); |
| if (suspendedPage && suspendedPage->pageIsClosedOrClosing()) |
| suspendedPage = nullptr; |
| |
| continueNavigationInNewProcess(navigation, WTFMove(suspendedPage), WTFMove(processForNavigation), processSwapRequestedByClient, ShouldTreatAsContinuingLoad::YesAfterNavigationPolicyDecision); |
| |
| receivedPolicyDecision(policyAction, navigation.ptr(), nullptr, WTFMove(navigationAction), WTFMove(sender), WillContinueLoadInNewProcess::Yes); |
| return; |
| } |
| |
| auto item = navigation->reloadItem() ? navigation->reloadItem() : navigation->targetItem(); |
| std::optional<SandboxExtension::Handle> optionalHandle; |
| if (policyAction == PolicyAction::Use && item) { |
| URL fullURL { item->url() }; |
| if (fullURL.protocolIs("file"_s)) { |
| SandboxExtension::Handle sandboxExtensionHandle; |
| maybeInitializeSandboxExtensionHandle(processForNavigation.get(), fullURL, item->resourceDirectoryURL(), sandboxExtensionHandle); |
| optionalHandle = WTFMove(sandboxExtensionHandle); |
| } |
| } |
| |
| receivedPolicyDecision(policyAction, navigation.ptr(), navigation->websitePolicies(), WTFMove(navigationAction), WTFMove(sender), WillContinueLoadInNewProcess::No, WTFMove(optionalHandle)); |
| }); |
| } |
| |
| void WebPageProxy::receivedPolicyDecision(PolicyAction action, API::Navigation* navigation, RefPtr<API::WebsitePolicies>&& websitePolicies, std::variant<Ref<API::NavigationResponse>, Ref<API::NavigationAction>>&& navigationActionOrResponse, Ref<PolicyDecisionSender>&& sender, WillContinueLoadInNewProcess willContinueLoadInNewProcess, std::optional<SandboxExtension::Handle> sandboxExtensionHandle) |
| { |
| if (!hasRunningProcess()) { |
| sender->send(PolicyDecision { sender->identifier(), isNavigatingToAppBoundDomain(), PolicyAction::Ignore, 0, std::nullopt, std::nullopt }); |
| return; |
| } |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| if (action == PolicyAction::Ignore && willContinueLoadInNewProcess == WillContinueLoadInNewProcess::No && navigation && navigation->navigationID() == m_pageLoadState.pendingAPIRequest().navigationID) |
| m_pageLoadState.clearPendingAPIRequest(transaction); |
| |
| std::optional<DownloadID> downloadID; |
| if (action == PolicyAction::Download) { |
| // Create a download proxy. |
| auto& download = m_process->processPool().createDownloadProxy(m_websiteDataStore, m_decidePolicyForResponseRequest, this, navigation ? navigation->originatingFrameInfo() : FrameInfoData { }); |
| download.setDidStartCallback([this, weakThis = WeakPtr { *this }, navigationActionOrResponse = WTFMove(navigationActionOrResponse)] (auto* downloadProxy) { |
| if (!weakThis || !downloadProxy) |
| return; |
| WTF::switchOn(navigationActionOrResponse, |
| [&] (const Ref<API::NavigationResponse>& response) { |
| if (!response->downloadAttribute().isNull()) |
| downloadProxy->setSuggestedFilename(response->downloadAttribute()); |
| m_navigationClient->navigationResponseDidBecomeDownload(*this, response.get(), *downloadProxy); |
| }, [&] (const Ref<API::NavigationAction>& action) { |
| m_navigationClient->navigationActionDidBecomeDownload(*this, action.get(), *downloadProxy); |
| } |
| ); |
| }); |
| if (navigation) { |
| download.setWasUserInitiated(navigation->wasUserInitiated()); |
| download.setRedirectChain(navigation->takeRedirectChain()); |
| } |
| |
| downloadID = download.downloadID(); |
| handleDownloadRequest(download); |
| m_decidePolicyForResponseRequest = { }; |
| } |
| |
| std::optional<WebsitePoliciesData> websitePoliciesData; |
| if (websitePolicies) |
| websitePoliciesData = websitePolicies->data(); |
| |
| sender->send(PolicyDecision { sender->identifier(), isNavigatingToAppBoundDomain(), action, navigation ? navigation->navigationID() : 0, downloadID, WTFMove(websitePoliciesData), WTFMove(sandboxExtensionHandle) }); |
| } |
| |
| void WebPageProxy::commitProvisionalPage(FrameIdentifier frameID, FrameInfoData&& frameInfo, ResourceRequest&& request, uint64_t navigationID, const String& mimeType, bool frameHasCustomContentProvider, WebCore::FrameLoadType frameLoadType, const WebCore::CertificateInfo& certificateInfo, bool usedLegacyTLS, bool containsPluginDocument, std::optional<WebCore::HasInsecureContent> forcedHasInsecureContent, WebCore::MouseEventPolicy mouseEventPolicy, const UserData& userData) |
| { |
| ASSERT(m_provisionalPage); |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "commitProvisionalPage: newPID=%i", m_provisionalPage->process().processIdentifier()); |
| |
| std::optional<FrameIdentifier> mainFrameIDInPreviousProcess = m_mainFrame ? std::make_optional(m_mainFrame->frameID()) : std::nullopt; |
| |
| ASSERT(m_process.ptr() != &m_provisionalPage->process()); |
| |
| auto shouldDelayClosingUntilFirstLayerFlush = ShouldDelayClosingUntilFirstLayerFlush::No; |
| #if PLATFORM(MAC) |
| // On macOS, when not using UI-side compositing, we need to make sure we do not close the page in the previous process until we've |
| // entered accelerated compositing for the new page or we will flash on navigation. |
| if (drawingArea()->type() == DrawingAreaType::TiledCoreAnimation) |
| shouldDelayClosingUntilFirstLayerFlush = ShouldDelayClosingUntilFirstLayerFlush::Yes; |
| #endif |
| |
| if (m_isLayerTreeFrozenDueToSwipeAnimation) |
| send(Messages::WebPage::UnfreezeLayerTreeDueToSwipeAnimation()); |
| |
| resetStateAfterProcessTermination(ProcessTerminationReason::NavigationSwap); |
| |
| removeAllMessageReceivers(); |
| auto* navigation = navigationState().navigation(m_provisionalPage->navigationID()); |
| bool didSuspendPreviousPage = navigation && !m_provisionalPage->isProcessSwappingOnNavigationResponse() ? suspendCurrentPageIfPossible(*navigation, mainFrameIDInPreviousProcess, m_provisionalPage->processSwapRequestedByClient(), shouldDelayClosingUntilFirstLayerFlush) : false; |
| m_process->removeWebPage(*this, m_websiteDataStore.ptr() == m_provisionalPage->process().websiteDataStore() ? WebProcessProxy::EndsUsingDataStore::No : WebProcessProxy::EndsUsingDataStore::Yes); |
| |
| // There is no way we'll be able to return to the page in the previous page so close it. |
| if (!didSuspendPreviousPage) |
| send(Messages::WebPage::Close()); |
| |
| const auto oldWebPageID = m_webPageID; |
| swapToProvisionalPage(std::exchange(m_provisionalPage, nullptr)); |
| |
| didCommitLoadForFrame(frameID, WTFMove(frameInfo), WTFMove(request), navigationID, mimeType, frameHasCustomContentProvider, frameLoadType, certificateInfo, usedLegacyTLS, containsPluginDocument, forcedHasInsecureContent, mouseEventPolicy, userData); |
| |
| m_inspectorController->didCommitProvisionalPage(oldWebPageID, m_webPageID); |
| } |
| |
| void WebPageProxy::destroyProvisionalPage() |
| { |
| m_provisionalPage = nullptr; |
| } |
| |
| void WebPageProxy::continueNavigationInNewProcess(API::Navigation& navigation, std::unique_ptr<SuspendedPageProxy>&& suspendedPage, Ref<WebProcessProxy>&& newProcess, ProcessSwapRequestedByClient processSwapRequestedByClient, ShouldTreatAsContinuingLoad shouldTreatAsContinuingLoad, std::optional<NetworkResourceLoadIdentifier> existingNetworkResourceLoadIdentifierToResume) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "continueNavigationInNewProcess: newProcessPID=%i, hasSuspendedPage=%i", newProcess->processIdentifier(), !!suspendedPage); |
| LOG(Loading, "Continuing navigation %" PRIu64 " '%s' in a new web process", navigation.navigationID(), navigation.loggingString()); |
| RELEASE_ASSERT(!newProcess->isInProcessCache()); |
| ASSERT(shouldTreatAsContinuingLoad != ShouldTreatAsContinuingLoad::No); |
| |
| if (m_provisionalPage) { |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "continueNavigationInNewProcess: There is already a pending provisional load, cancelling it (provisonalNavigationID=%llu, navigationID=%llu)", m_provisionalPage->navigationID(), navigation.navigationID()); |
| if (m_provisionalPage->navigationID() != navigation.navigationID()) |
| m_provisionalPage->cancel(); |
| m_provisionalPage = nullptr; |
| } |
| |
| RefPtr websitePolicies = navigation.websitePolicies(); |
| bool isServerSideRedirect = shouldTreatAsContinuingLoad == ShouldTreatAsContinuingLoad::YesAfterNavigationPolicyDecision && navigation.currentRequestIsRedirect(); |
| bool isProcessSwappingOnNavigationResponse = shouldTreatAsContinuingLoad == ShouldTreatAsContinuingLoad::YesAfterProvisionalLoadStarted; |
| m_provisionalPage = makeUnique<ProvisionalPageProxy>(*this, WTFMove(newProcess), WTFMove(suspendedPage), navigation.navigationID(), isServerSideRedirect, navigation.currentRequest(), processSwapRequestedByClient, isProcessSwappingOnNavigationResponse, websitePolicies.get()); |
| auto continuation = [this, protectedThis = Ref { *this }, navigation = Ref { navigation }, shouldTreatAsContinuingLoad, websitePolicies = WTFMove(websitePolicies), existingNetworkResourceLoadIdentifierToResume]() mutable { |
| if (auto* item = navigation->targetItem()) { |
| LOG(Loading, "WebPageProxy %p continueNavigationInNewProcess to back item URL %s", this, item->url().utf8().data()); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setPendingAPIRequest(transaction, { navigation->navigationID(), item->url() }); |
| |
| m_provisionalPage->goToBackForwardItem(navigation, *item, WTFMove(websitePolicies), shouldTreatAsContinuingLoad, existingNetworkResourceLoadIdentifierToResume); |
| return; |
| } |
| |
| if (m_backForwardList->currentItem() && (navigation->lockBackForwardList() == LockBackForwardList::Yes || navigation->lockHistory() == LockHistory::Yes)) { |
| // If WebCore is supposed to lock the history for this load, then the new process needs to know about the current history item so it can update |
| // it instead of creating a new one. |
| m_provisionalPage->send(Messages::WebPage::SetCurrentHistoryItemForReattach(m_backForwardList->currentItem()->itemState())); |
| } |
| |
| std::optional<WebsitePoliciesData> websitePoliciesData; |
| if (websitePolicies) |
| websitePoliciesData = websitePolicies->data(); |
| |
| // FIXME: Work out timing of responding with the last policy delegate, etc |
| ASSERT(!navigation->currentRequest().isEmpty()); |
| ASSERT(!existingNetworkResourceLoadIdentifierToResume || !navigation->substituteData()); |
| if (auto& substituteData = navigation->substituteData()) |
| m_provisionalPage->loadData(navigation, { substituteData->content.data(), substituteData->content.size() }, substituteData->MIMEType, substituteData->encoding, substituteData->baseURL, substituteData->userData.get(), shouldTreatAsContinuingLoad, isNavigatingToAppBoundDomain(), WTFMove(websitePoliciesData), substituteData->sessionHistoryVisibility); |
| else |
| m_provisionalPage->loadRequest(navigation, ResourceRequest { navigation->currentRequest() }, nullptr, shouldTreatAsContinuingLoad, isNavigatingToAppBoundDomain(), WTFMove(websitePoliciesData), existingNetworkResourceLoadIdentifierToResume); |
| }; |
| if (m_inspectorController->shouldPauseLoading(*m_provisionalPage)) |
| m_inspectorController->setContinueLoadingCallback(*m_provisionalPage, WTFMove(continuation)); |
| else |
| continuation(); |
| } |
| |
| bool WebPageProxy::isPageOpenedByDOMShowingInitialEmptyDocument() const |
| { |
| return openedByDOM() && !hasCommittedAnyProvisionalLoads(); |
| } |
| |
| // MSVC gives a redeclaration error if noreturn is used on the definition and not the declaration, while |
| // Cocoa tests segfault if it is moved to the declaration site (even if we move the definition with it!). |
| #if !COMPILER(MSVC) |
| NO_RETURN_DUE_TO_ASSERT |
| #endif |
| void WebPageProxy::didFailToSuspendAfterProcessSwap() |
| { |
| // Only the SuspendedPageProxy should be getting this call. |
| ASSERT_NOT_REACHED(); |
| } |
| |
| #if !COMPILER(MSVC) |
| NO_RETURN_DUE_TO_ASSERT |
| #endif |
| void WebPageProxy::didSuspendAfterProcessSwap() |
| { |
| // Only the SuspendedPageProxy should be getting this call. |
| ASSERT_NOT_REACHED(); |
| } |
| |
| void WebPageProxy::setUserAgent(String&& userAgent) |
| { |
| if (m_userAgent == userAgent) |
| return; |
| m_userAgent = WTFMove(userAgent); |
| |
| // We update the service worker there at the moment to be sure we use values used by actual web pages. |
| // FIXME: Refactor this when we have a better User-Agent story. |
| process().processPool().updateRemoteWorkerUserAgent(m_userAgent); |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetUserAgent(m_userAgent)); |
| } |
| |
| void WebPageProxy::setApplicationNameForUserAgent(const String& applicationName) |
| { |
| if (m_applicationNameForUserAgent == applicationName) |
| return; |
| |
| m_applicationNameForUserAgent = applicationName; |
| if (!m_customUserAgent.isEmpty()) |
| return; |
| |
| setUserAgent(standardUserAgent(m_applicationNameForUserAgent)); |
| } |
| |
| void WebPageProxy::setCustomUserAgent(const String& customUserAgent) |
| { |
| if (m_customUserAgent == customUserAgent) |
| return; |
| |
| m_customUserAgent = customUserAgent; |
| |
| if (m_customUserAgent.isEmpty()) { |
| setUserAgent(standardUserAgent(m_applicationNameForUserAgent)); |
| return; |
| } |
| |
| setUserAgent(String { m_customUserAgent }); |
| } |
| |
| void WebPageProxy::resumeActiveDOMObjectsAndAnimations() |
| { |
| if (!hasRunningProcess() || !m_areActiveDOMObjectsAndAnimationsSuspended) |
| return; |
| |
| m_areActiveDOMObjectsAndAnimationsSuspended = false; |
| |
| send(Messages::WebPage::ResumeActiveDOMObjectsAndAnimations()); |
| } |
| |
| void WebPageProxy::suspendActiveDOMObjectsAndAnimations() |
| { |
| if (!hasRunningProcess() || m_areActiveDOMObjectsAndAnimationsSuspended) |
| return; |
| |
| m_areActiveDOMObjectsAndAnimationsSuspended = true; |
| |
| send(Messages::WebPage::SuspendActiveDOMObjectsAndAnimations()); |
| } |
| |
| void WebPageProxy::suspend(CompletionHandler<void(bool)>&& completionHandler) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "suspend:"); |
| if (!hasRunningProcess() || m_isSuspended) |
| return completionHandler(false); |
| |
| m_isSuspended = true; |
| sendWithAsyncReply(Messages::WebPage::Suspend(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::resume(CompletionHandler<void(bool)>&& completionHandler) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "resume:"); |
| |
| if (!hasRunningProcess() || !m_isSuspended) |
| return completionHandler(false); |
| |
| m_isSuspended = false; |
| sendWithAsyncReply(Messages::WebPage::Resume(), WTFMove(completionHandler)); |
| } |
| |
| bool WebPageProxy::supportsTextEncoding() const |
| { |
| // FIXME (118840): We should probably only support this for text documents, not all non-image documents. |
| return m_mainFrame && !m_mainFrame->isDisplayingStandaloneImageDocument(); |
| } |
| |
| void WebPageProxy::setCustomTextEncodingName(const String& encodingName) |
| { |
| if (m_customTextEncodingName == encodingName) |
| return; |
| m_customTextEncodingName = encodingName; |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetCustomTextEncodingName(encodingName)); |
| } |
| |
| SessionState WebPageProxy::sessionState(WTF::Function<bool (WebBackForwardListItem&)>&& filter) const |
| { |
| RELEASE_ASSERT(RunLoop::isMain()); |
| SessionState sessionState; |
| |
| sessionState.backForwardListState = m_backForwardList->backForwardListState(WTFMove(filter)); |
| |
| String provisionalURLString = m_pageLoadState.pendingAPIRequestURL(); |
| if (provisionalURLString.isEmpty()) |
| provisionalURLString = m_pageLoadState.provisionalURL(); |
| |
| if (!provisionalURLString.isEmpty()) |
| sessionState.provisionalURL = URL { provisionalURLString }; |
| |
| sessionState.renderTreeSize = renderTreeSize(); |
| sessionState.isAppInitiated = m_lastNavigationWasAppInitiated; |
| return sessionState; |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::restoreFromSessionState(SessionState sessionState, bool navigate) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "restoreFromSessionState:"); |
| |
| m_lastNavigationWasAppInitiated = sessionState.isAppInitiated; |
| m_sessionRestorationRenderTreeSize = 0; |
| m_hitRenderTreeSizeThreshold = false; |
| |
| bool hasBackForwardList = !!sessionState.backForwardListState.currentIndex; |
| |
| if (hasBackForwardList) { |
| m_sessionStateWasRestoredByAPIRequest = true; |
| |
| m_backForwardList->restoreFromState(WTFMove(sessionState.backForwardListState)); |
| // If the process is not launched yet, the session will be restored when sending the WebPageCreationParameters; |
| if (hasRunningProcess()) |
| send(Messages::WebPage::RestoreSession(m_backForwardList->itemStates())); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setCanGoBack(transaction, m_backForwardList->backItem()); |
| m_pageLoadState.setCanGoForward(transaction, m_backForwardList->forwardItem()); |
| |
| // The back / forward list was restored from a sessionState so we don't want to snapshot the current |
| // page when navigating away. Suppress navigation snapshotting until the next load has committed |
| suppressNextAutomaticNavigationSnapshot(); |
| } |
| |
| // FIXME: Navigating should be separate from state restoration. |
| if (navigate) { |
| m_sessionRestorationRenderTreeSize = sessionState.renderTreeSize; |
| if (!m_sessionRestorationRenderTreeSize) |
| m_hitRenderTreeSizeThreshold = true; // If we didn't get data on renderTreeSize, just don't fire the milestone. |
| |
| if (!sessionState.provisionalURL.isNull()) |
| return loadRequest(sessionState.provisionalURL); |
| |
| if (hasBackForwardList) { |
| if (WebBackForwardListItem* item = m_backForwardList->currentItem()) |
| return goToBackForwardItem(*item); |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| bool WebPageProxy::supportsTextZoom() const |
| { |
| // FIXME (118840): This should also return false for standalone media and plug-in documents. |
| if (!m_mainFrame || m_mainFrame->isDisplayingStandaloneImageDocument()) |
| return false; |
| |
| return true; |
| } |
| |
| void WebPageProxy::setTextZoomFactor(double zoomFactor) |
| { |
| if (m_textZoomFactor == zoomFactor) |
| return; |
| |
| m_textZoomFactor = zoomFactor; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetTextZoomFactor(m_textZoomFactor)); |
| } |
| |
| void WebPageProxy::setPageZoomFactor(double zoomFactor) |
| { |
| if (m_pageZoomFactor == zoomFactor) |
| return; |
| |
| closeOverlayedViews(); |
| |
| m_pageZoomFactor = zoomFactor; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetPageZoomFactor(m_pageZoomFactor)); |
| } |
| |
| void WebPageProxy::setPageAndTextZoomFactors(double pageZoomFactor, double textZoomFactor) |
| { |
| if (m_pageZoomFactor == pageZoomFactor && m_textZoomFactor == textZoomFactor) |
| return; |
| |
| closeOverlayedViews(); |
| |
| m_pageZoomFactor = pageZoomFactor; |
| m_textZoomFactor = textZoomFactor; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetPageAndTextZoomFactors(m_pageZoomFactor, m_textZoomFactor)); |
| } |
| |
| double WebPageProxy::pageZoomFactor() const |
| { |
| // Zoom factor for non-PDF pages persists across page loads. We maintain a separate member variable for PDF |
| // zoom which ensures that we don't use the PDF zoom for a normal page. |
| if (m_mainFramePluginHandlesPageScaleGesture) |
| return m_pluginZoomFactor; |
| return m_pageZoomFactor; |
| } |
| |
| double WebPageProxy::pageScaleFactor() const |
| { |
| // PDF documents use zoom and scale factors to size themselves appropriately in the window. We store them |
| // separately but decide which to return based on the main frame. |
| if (m_mainFramePluginHandlesPageScaleGesture) |
| return m_pluginScaleFactor; |
| return m_pageScaleFactor; |
| } |
| |
| void WebPageProxy::scalePage(double scale, const IntPoint& origin) |
| { |
| ASSERT(scale > 0); |
| |
| m_pageScaleFactor = scale; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ScalePage(scale, origin)); |
| } |
| |
| void WebPageProxy::scalePageInViewCoordinates(double scale, const IntPoint& centerInViewCoordinates) |
| { |
| ASSERT(scale > 0); |
| |
| m_pageScaleFactor = scale; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ScalePageInViewCoordinates(scale, centerInViewCoordinates)); |
| } |
| |
| void WebPageProxy::scaleView(double scale) |
| { |
| ASSERT(scale > 0); |
| |
| m_viewScaleFactor = scale; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ScaleView(scale)); |
| } |
| |
| void WebPageProxy::setIntrinsicDeviceScaleFactor(float scaleFactor) |
| { |
| if (m_intrinsicDeviceScaleFactor == scaleFactor) |
| return; |
| |
| m_intrinsicDeviceScaleFactor = scaleFactor; |
| |
| if (m_drawingArea) |
| m_drawingArea->deviceScaleFactorDidChange(); |
| } |
| |
| void WebPageProxy::windowScreenDidChange(PlatformDisplayID displayID, std::optional<unsigned> nominalFramesPerSecond) |
| { |
| #if HAVE(CVDISPLAYLINK) |
| if (hasRunningProcess() && m_displayID && m_registeredForFullSpeedUpdates) |
| process().processPool().setDisplayLinkForDisplayWantsFullSpeedUpdates(*m_process->connection(), *m_displayID, false); |
| |
| m_registeredForFullSpeedUpdates = false; |
| #endif |
| |
| m_displayID = displayID; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::EventDispatcher::PageScreenDidChange(m_webPageID, displayID, nominalFramesPerSecond)); |
| send(Messages::WebPage::WindowScreenDidChange(displayID, nominalFramesPerSecond)); |
| #if HAVE(CVDISPLAYLINK) |
| updateDisplayLinkFrequency(); |
| #endif |
| } |
| |
| float WebPageProxy::deviceScaleFactor() const |
| { |
| return m_customDeviceScaleFactor.value_or(m_intrinsicDeviceScaleFactor); |
| } |
| |
| void WebPageProxy::setCustomDeviceScaleFactor(float customScaleFactor) |
| { |
| if (m_customDeviceScaleFactor && m_customDeviceScaleFactor.value() == customScaleFactor) |
| return; |
| |
| float oldScaleFactor = deviceScaleFactor(); |
| |
| // A value of 0 clears the customScaleFactor. |
| if (customScaleFactor) |
| m_customDeviceScaleFactor = customScaleFactor; |
| else |
| m_customDeviceScaleFactor = std::nullopt; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| if (deviceScaleFactor() != oldScaleFactor) |
| m_drawingArea->deviceScaleFactorDidChange(); |
| } |
| |
| void WebPageProxy::accessibilitySettingsDidChange() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| // Also update screen properties which encodes invert colors. |
| process().processPool().screenPropertiesStateChanged(); |
| send(Messages::WebPage::AccessibilitySettingsDidChange()); |
| } |
| |
| void WebPageProxy::setUseFixedLayout(bool fixed) |
| { |
| // This check is fine as the value is initialized in the web |
| // process as part of the creation parameters. |
| if (fixed == m_useFixedLayout) |
| return; |
| |
| m_useFixedLayout = fixed; |
| if (!fixed) |
| m_fixedLayoutSize = IntSize(); |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetUseFixedLayout(fixed)); |
| } |
| |
| void WebPageProxy::setFixedLayoutSize(const IntSize& size) |
| { |
| if (size == m_fixedLayoutSize) |
| return; |
| |
| m_fixedLayoutSize = size; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetFixedLayoutSize(size)); |
| } |
| |
| void WebPageProxy::setDefaultUnobscuredSize(const FloatSize& size) |
| { |
| if (size == m_defaultUnobscuredSize) |
| return; |
| |
| m_defaultUnobscuredSize = size; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetDefaultUnobscuredSize(m_defaultUnobscuredSize)); |
| } |
| |
| void WebPageProxy::setMinimumUnobscuredSize(const FloatSize& size) |
| { |
| if (size == m_minimumUnobscuredSize) |
| return; |
| |
| m_minimumUnobscuredSize = size; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetMinimumUnobscuredSize(m_minimumUnobscuredSize)); |
| } |
| |
| void WebPageProxy::setMaximumUnobscuredSize(const FloatSize& size) |
| { |
| if (size == m_maximumUnobscuredSize) |
| return; |
| |
| m_maximumUnobscuredSize = size; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetMaximumUnobscuredSize(m_maximumUnobscuredSize)); |
| } |
| |
| void WebPageProxy::setViewExposedRect(std::optional<WebCore::FloatRect> viewExposedRect) |
| { |
| if (m_viewExposedRect == viewExposedRect) |
| return; |
| |
| m_viewExposedRect = viewExposedRect; |
| |
| #if PLATFORM(MAC) |
| if (m_drawingArea) |
| m_drawingArea->didChangeViewExposedRect(); |
| #endif |
| } |
| |
| void WebPageProxy::setAlwaysShowsHorizontalScroller(bool alwaysShowsHorizontalScroller) |
| { |
| if (alwaysShowsHorizontalScroller == m_alwaysShowsHorizontalScroller) |
| return; |
| |
| m_alwaysShowsHorizontalScroller = alwaysShowsHorizontalScroller; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetAlwaysShowsHorizontalScroller(alwaysShowsHorizontalScroller)); |
| } |
| |
| void WebPageProxy::setAlwaysShowsVerticalScroller(bool alwaysShowsVerticalScroller) |
| { |
| if (alwaysShowsVerticalScroller == m_alwaysShowsVerticalScroller) |
| return; |
| |
| m_alwaysShowsVerticalScroller = alwaysShowsVerticalScroller; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetAlwaysShowsVerticalScroller(alwaysShowsVerticalScroller)); |
| } |
| |
| void WebPageProxy::listenForLayoutMilestones(OptionSet<WebCore::LayoutMilestone> milestones) |
| { |
| if (milestones == m_observedLayoutMilestones) |
| return; |
| |
| m_observedLayoutMilestones = milestones; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ListenForLayoutMilestones(milestones)); |
| } |
| |
| void WebPageProxy::setSuppressScrollbarAnimations(bool suppressAnimations) |
| { |
| if (suppressAnimations == m_suppressScrollbarAnimations) |
| return; |
| |
| m_suppressScrollbarAnimations = suppressAnimations; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetSuppressScrollbarAnimations(suppressAnimations)); |
| } |
| |
| void WebPageProxy::setRubberBandsAtLeft(bool rubberBandsAtLeft) |
| { |
| m_rubberBandableEdges.setLeft(rubberBandsAtLeft); |
| } |
| |
| void WebPageProxy::setRubberBandsAtRight(bool rubberBandsAtRight) |
| { |
| m_rubberBandableEdges.setRight(rubberBandsAtRight); |
| } |
| |
| void WebPageProxy::setRubberBandsAtTop(bool rubberBandsAtTop) |
| { |
| m_rubberBandableEdges.setTop(rubberBandsAtTop); |
| } |
| |
| void WebPageProxy::setRubberBandsAtBottom(bool rubberBandsAtBottom) |
| { |
| m_rubberBandableEdges.setBottom(rubberBandsAtBottom); |
| } |
| |
| void WebPageProxy::setEnableVerticalRubberBanding(bool enableVerticalRubberBanding) |
| { |
| if (enableVerticalRubberBanding == m_enableVerticalRubberBanding) |
| return; |
| |
| m_enableVerticalRubberBanding = enableVerticalRubberBanding; |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetEnableVerticalRubberBanding(enableVerticalRubberBanding)); |
| } |
| |
| bool WebPageProxy::verticalRubberBandingIsEnabled() const |
| { |
| return m_enableVerticalRubberBanding; |
| } |
| |
| void WebPageProxy::setEnableHorizontalRubberBanding(bool enableHorizontalRubberBanding) |
| { |
| if (enableHorizontalRubberBanding == m_enableHorizontalRubberBanding) |
| return; |
| |
| m_enableHorizontalRubberBanding = enableHorizontalRubberBanding; |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetEnableHorizontalRubberBanding(enableHorizontalRubberBanding)); |
| } |
| |
| bool WebPageProxy::horizontalRubberBandingIsEnabled() const |
| { |
| return m_enableHorizontalRubberBanding; |
| } |
| |
| void WebPageProxy::setBackgroundExtendsBeyondPage(bool backgroundExtendsBeyondPage) |
| { |
| if (backgroundExtendsBeyondPage == m_backgroundExtendsBeyondPage) |
| return; |
| |
| m_backgroundExtendsBeyondPage = backgroundExtendsBeyondPage; |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetBackgroundExtendsBeyondPage(backgroundExtendsBeyondPage)); |
| } |
| |
| bool WebPageProxy::backgroundExtendsBeyondPage() const |
| { |
| return m_backgroundExtendsBeyondPage; |
| } |
| |
| void WebPageProxy::setPaginationMode(WebCore::Pagination::Mode mode) |
| { |
| if (mode == m_paginationMode) |
| return; |
| |
| m_paginationMode = mode; |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetPaginationMode(mode)); |
| } |
| |
| void WebPageProxy::setPaginationBehavesLikeColumns(bool behavesLikeColumns) |
| { |
| if (behavesLikeColumns == m_paginationBehavesLikeColumns) |
| return; |
| |
| m_paginationBehavesLikeColumns = behavesLikeColumns; |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetPaginationBehavesLikeColumns(behavesLikeColumns)); |
| } |
| |
| void WebPageProxy::setPageLength(double pageLength) |
| { |
| if (pageLength == m_pageLength) |
| return; |
| |
| m_pageLength = pageLength; |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetPageLength(pageLength)); |
| } |
| |
| void WebPageProxy::setGapBetweenPages(double gap) |
| { |
| if (gap == m_gapBetweenPages) |
| return; |
| |
| m_gapBetweenPages = gap; |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetGapBetweenPages(gap)); |
| } |
| |
| void WebPageProxy::setPaginationLineGridEnabled(bool lineGridEnabled) |
| { |
| if (lineGridEnabled == m_paginationLineGridEnabled) |
| return; |
| |
| m_paginationLineGridEnabled = lineGridEnabled; |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::SetPaginationLineGridEnabled(lineGridEnabled)); |
| } |
| |
| void WebPageProxy::pageScaleFactorDidChange(double scaleFactor) |
| { |
| m_pageScaleFactor = scaleFactor; |
| } |
| |
| void WebPageProxy::pluginScaleFactorDidChange(double pluginScaleFactor) |
| { |
| m_pluginScaleFactor = pluginScaleFactor; |
| } |
| |
| void WebPageProxy::pluginZoomFactorDidChange(double pluginZoomFactor) |
| { |
| m_pluginZoomFactor = pluginZoomFactor; |
| } |
| |
| void WebPageProxy::findStringMatches(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount) |
| { |
| if (string.isEmpty()) { |
| didFindStringMatches(string, Vector<Vector<WebCore::IntRect>> (), 0); |
| return; |
| } |
| |
| send(Messages::WebPage::FindStringMatches(string, options, maxMatchCount)); |
| } |
| |
| void WebPageProxy::findString(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount, CompletionHandler<void(bool)>&& callbackFunction) |
| { |
| sendWithAsyncReply(Messages::WebPage::FindString(string, options, maxMatchCount), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::findRectsForStringMatches(const String& string, OptionSet<WebKit::FindOptions> options, unsigned maxMatchCount, CompletionHandler<void(Vector<WebCore::FloatRect>&&)>&& callbackFunction) |
| { |
| sendWithAsyncReply(Messages::WebPage::FindRectsForStringMatches(string, options, maxMatchCount), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::findTextRangesForStringMatches(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount, CompletionHandler<void(Vector<WebFoundTextRange>&&)>&& callbackFunction) |
| { |
| sendWithAsyncReply(Messages::WebPage::FindTextRangesForStringMatches(string, options, maxMatchCount), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::replaceFoundTextRangeWithString(const WebFoundTextRange& range, const String& string) |
| { |
| send(Messages::WebPage::ReplaceFoundTextRangeWithString(range, string)); |
| } |
| |
| void WebPageProxy::decorateTextRangeWithStyle(const WebFoundTextRange& range, FindDecorationStyle style) |
| { |
| send(Messages::WebPage::DecorateTextRangeWithStyle(range, style)); |
| } |
| |
| void WebPageProxy::scrollTextRangeToVisible(const WebFoundTextRange& range) |
| { |
| send(Messages::WebPage::ScrollTextRangeToVisible(range)); |
| } |
| |
| void WebPageProxy::clearAllDecoratedFoundText() |
| { |
| send(Messages::WebPage::ClearAllDecoratedFoundText()); |
| } |
| |
| void WebPageProxy::didBeginTextSearchOperation() |
| { |
| send(Messages::WebPage::DidBeginTextSearchOperation()); |
| } |
| |
| void WebPageProxy::didEndTextSearchOperation() |
| { |
| send(Messages::WebPage::DidEndTextSearchOperation()); |
| } |
| |
| void WebPageProxy::requestRectForFoundTextRange(const WebFoundTextRange& range, CompletionHandler<void(WebCore::FloatRect)>&& callbackFunction) |
| { |
| sendWithAsyncReply(Messages::WebPage::RequestRectForFoundTextRange(range), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::addLayerForFindOverlay(CompletionHandler<void(WebCore::GraphicsLayer::PlatformLayerID)>&& callbackFunction) |
| { |
| sendWithAsyncReply(Messages::WebPage::AddLayerForFindOverlay(), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::removeLayerForFindOverlay(CompletionHandler<void()>&& callbackFunction) |
| { |
| sendWithAsyncReply(Messages::WebPage::RemoveLayerForFindOverlay(), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::getImageForFindMatch(int32_t matchIndex) |
| { |
| send(Messages::WebPage::GetImageForFindMatch(matchIndex)); |
| } |
| |
| void WebPageProxy::selectFindMatch(int32_t matchIndex) |
| { |
| send(Messages::WebPage::SelectFindMatch(matchIndex)); |
| } |
| |
| void WebPageProxy::indicateFindMatch(int32_t matchIndex) |
| { |
| send(Messages::WebPage::IndicateFindMatch(matchIndex)); |
| } |
| |
| void WebPageProxy::hideFindUI() |
| { |
| send(Messages::WebPage::HideFindUI()); |
| } |
| |
| void WebPageProxy::hideFindIndicator() |
| { |
| send(Messages::WebPage::HideFindIndicator()); |
| } |
| |
| void WebPageProxy::countStringMatches(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::CountStringMatches(string, options, maxMatchCount)); |
| } |
| |
| void WebPageProxy::replaceMatches(Vector<uint32_t>&& matchIndices, const String& replacementText, bool selectionOnly, CompletionHandler<void(uint64_t)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::ReplaceMatches(WTFMove(matchIndices), replacementText, selectionOnly), WTFMove(callback)); |
| } |
| |
| void WebPageProxy::launchInitialProcessIfNecessary() |
| { |
| if (process().isDummyProcessProxy()) |
| launchProcess({ }, ProcessLaunchReason::InitialProcess); |
| } |
| |
| void WebPageProxy::runJavaScriptInMainFrame(RunJavaScriptParameters&& parameters, CompletionHandler<void(Expected<RefPtr<API::SerializedScriptValue>, WebCore::ExceptionDetails>&&)>&& callbackFunction) |
| { |
| runJavaScriptInFrameInScriptWorld(WTFMove(parameters), std::nullopt, API::ContentWorld::pageContentWorld(), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::runJavaScriptInFrameInScriptWorld(RunJavaScriptParameters&& parameters, std::optional<WebCore::FrameIdentifier> frameID, API::ContentWorld& world, CompletionHandler<void(Expected<RefPtr<API::SerializedScriptValue>, WebCore::ExceptionDetails>&&)>&& callbackFunction) |
| { |
| // For backward-compatibility support running script in a WebView which has not done any loads yets. |
| launchInitialProcessIfNecessary(); |
| |
| if (!hasRunningProcess()) |
| return callbackFunction({ nullptr }); |
| |
| ProcessThrottler::ActivityVariant activity; |
| #if PLATFORM(IOS_FAMILY) |
| if (pageClient().canTakeForegroundAssertions()) |
| activity = m_process->throttler().foregroundActivity("WebPageProxy::runJavaScriptInFrameInScriptWorld"_s); |
| #endif |
| |
| sendWithAsyncReply(Messages::WebPage::RunJavaScriptInFrameInScriptWorld(parameters, frameID, world.worldData()), [activity = WTFMove(activity), callbackFunction = WTFMove(callbackFunction)] (const IPC::DataReference& dataReference, std::optional<ExceptionDetails>&& details) mutable { |
| if (details) |
| return callbackFunction(makeUnexpected(WTFMove(*details))); |
| if (dataReference.empty()) |
| return callbackFunction({ nullptr }); |
| callbackFunction({ API::SerializedScriptValue::createFromWireBytes(Vector(dataReference)).ptr() }); |
| }); |
| } |
| |
| void WebPageProxy::getRenderTreeExternalRepresentation(CompletionHandler<void(const String&)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetRenderTreeExternalRepresentation(), WTFMove(callback)); |
| } |
| |
| void WebPageProxy::getSourceForFrame(WebFrameProxy* frame, CompletionHandler<void(const String&)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetSourceForFrame(frame->frameID()), WTFMove(callback)); |
| } |
| |
| void WebPageProxy::getContentsAsString(ContentAsStringIncludesChildFrames includesChildFrames, CompletionHandler<void(const String&)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetContentsAsString(includesChildFrames), WTFMove(callback)); |
| } |
| |
| #if PLATFORM(COCOA) |
| void WebPageProxy::getContentsAsAttributedString(CompletionHandler<void(const WebCore::AttributedString&)>&& completionHandler) |
| { |
| if (!hasRunningProcess()) { |
| completionHandler({ }); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::GetContentsAsAttributedString(), WTFMove(completionHandler)); |
| } |
| #endif |
| |
| void WebPageProxy::getAllFrames(CompletionHandler<void(FrameTreeNodeData&&)>&& completionHandler) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetAllFrames(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::getBytecodeProfile(CompletionHandler<void(const String&)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetBytecodeProfile(), WTFMove(callback)); |
| } |
| |
| void WebPageProxy::getSamplingProfilerOutput(CompletionHandler<void(const String&)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetSamplingProfilerOutput(), WTFMove(callback)); |
| } |
| |
| template <typename T> |
| static CompletionHandler<void(T data)> toAPIDataCallbackT(CompletionHandler<void(API::Data*)>&& callback) |
| { |
| return [callback = WTFMove(callback)] (T data) mutable { |
| if (!data) { |
| callback(nullptr); |
| return; |
| } |
| callback(API::Data::create(data->data(), data->size()).ptr()); |
| }; |
| } |
| |
| auto* toAPIDataCallback = toAPIDataCallbackT<const std::optional<IPC::SharedBufferReference>&>; |
| auto* toAPIDataSharedBufferCallback = toAPIDataCallbackT<RefPtr<WebCore::SharedBuffer>&&>; |
| |
| #if ENABLE(MHTML) |
| void WebPageProxy::getContentsAsMHTMLData(CompletionHandler<void(API::Data*)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetContentsAsMHTMLData(), toAPIDataCallback(WTFMove(callback))); |
| } |
| #endif |
| |
| void WebPageProxy::getSelectionOrContentsAsString(CompletionHandler<void(const String&)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetSelectionOrContentsAsString(), callback); |
| } |
| |
| void WebPageProxy::getSelectionAsWebArchiveData(CompletionHandler<void(API::Data*)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetSelectionAsWebArchiveData(), toAPIDataCallback(WTFMove(callback))); |
| } |
| |
| void WebPageProxy::getMainResourceDataOfFrame(WebFrameProxy* frame, CompletionHandler<void(API::Data*)>&& callback) |
| { |
| if (!frame) |
| return callback(nullptr); |
| sendWithAsyncReply(Messages::WebPage::GetMainResourceDataOfFrame(frame->frameID()), toAPIDataCallback(WTFMove(callback))); |
| } |
| |
| void WebPageProxy::getResourceDataFromFrame(WebFrameProxy& frame, API::URL* resourceURL, CompletionHandler<void(API::Data*)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetResourceDataFromFrame(frame.frameID(), resourceURL->string()), toAPIDataCallback(WTFMove(callback))); |
| } |
| |
| void WebPageProxy::getWebArchiveOfFrame(WebFrameProxy* frame, CompletionHandler<void(API::Data*)>&& callback) |
| { |
| if (!frame) |
| return callback(nullptr); |
| sendWithAsyncReply(Messages::WebPage::GetWebArchiveOfFrame(frame->frameID()), toAPIDataCallback(WTFMove(callback))); |
| } |
| |
| void WebPageProxy::getAccessibilityTreeData(CompletionHandler<void(API::Data*)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetAccessibilityTreeData(), toAPIDataCallback(WTFMove(callback))); |
| } |
| |
| void WebPageProxy::forceRepaint(CompletionHandler<void()>&& callback) |
| { |
| if (!hasRunningProcess()) |
| return callback(); |
| |
| m_drawingArea->waitForBackingStoreUpdateOnNextPaint(); |
| |
| sendWithAsyncReply(Messages::WebPage::ForceRepaint(), [weakThis = WeakPtr { *this }, callback = WTFMove(callback)] () mutable { |
| if (weakThis) { |
| weakThis->callAfterNextPresentationUpdate([callback = WTFMove(callback)] (auto) mutable { |
| callback(); |
| }); |
| } else |
| callback(); |
| }); |
| } |
| |
| static OptionSet<IPC::SendOption> printingSendOptions(bool isPerformingDOMPrintOperation) |
| { |
| if (isPerformingDOMPrintOperation) |
| return IPC::SendOption::DispatchMessageEvenWhenWaitingForUnboundedSyncReply; |
| |
| return { }; |
| } |
| |
| void WebPageProxy::preferencesDidChange() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| updateThrottleState(); |
| updateHiddenPageThrottlingAutoIncreases(); |
| |
| pageClient().preferencesDidChange(); |
| |
| // FIXME: It probably makes more sense to send individual preference changes. |
| // However, WebKitTestRunner depends on getting a preference change notification |
| // even if nothing changed in UI process, so that overrides get removed. |
| |
| // Preferences need to be updated during synchronous printing to make "print backgrounds" preference work when toggled from a print dialog checkbox. |
| send(Messages::WebPage::PreferencesDidChange(preferencesStore()), printingSendOptions(m_isPerformingDOMPrintOperation)); |
| } |
| |
| void WebPageProxy::didCreateMainFrame(FrameIdentifier frameID) |
| { |
| // The DecidePolicyForNavigationActionSync IPC is synchronous and may therefore get processed before the DidCreateMainFrame one. |
| // When this happens, decidePolicyForNavigationActionSync() calls didCreateMainFrame() and we need to ignore the DidCreateMainFrame |
| // IPC when it later gets processed. |
| if (m_mainFrame && m_mainFrame->frameID() == frameID) |
| return; |
| |
| PageClientProtector protector(pageClient()); |
| |
| MESSAGE_CHECK(m_process, !m_mainFrame); |
| MESSAGE_CHECK(m_process, m_process->canCreateFrame(frameID)); |
| |
| m_mainFrame = WebFrameProxy::create(*this, frameID); |
| |
| #if ENABLE(SERVICE_WORKER) |
| if (m_serviceWorkerOpenWindowCompletionCallback) |
| m_mainFrame->setNavigationCallback(WTFMove(m_serviceWorkerOpenWindowCompletionCallback)); |
| #endif |
| |
| // Add the frame to the process wide map. |
| m_process->frameCreated(frameID, *m_mainFrame); |
| } |
| |
| void WebPageProxy::didCreateSubframe(FrameIdentifier frameID) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| MESSAGE_CHECK(m_process, m_mainFrame); |
| |
| // The DecidePolicyForNavigationActionSync IPC is synchronous and may therefore get processed before the DidCreateSubframe one. |
| // When this happens, decidePolicyForNavigationActionSync() calls didCreateSubframe() and we need to ignore the DidCreateSubframe |
| // IPC when it later gets processed. |
| if (m_process->webFrame(frameID)) |
| return; |
| |
| MESSAGE_CHECK(m_process, m_process->canCreateFrame(frameID)); |
| |
| auto subFrame = WebFrameProxy::create(*this, frameID); |
| |
| // Add the frame to the process wide map. |
| m_process->frameCreated(frameID, subFrame.get()); |
| } |
| |
| void WebPageProxy::didCreateWindow(FrameIdentifier frameID, GlobalWindowIdentifier&& windowIdentifier) |
| { |
| } |
| |
| double WebPageProxy::estimatedProgress() const |
| { |
| return m_pageLoadState.estimatedProgress(); |
| } |
| |
| void WebPageProxy::didStartProgress() |
| { |
| ASSERT(!m_isClosed); |
| |
| PageClientProtector protector(pageClient()); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.didStartProgress(transaction); |
| |
| m_pageLoadState.commitChanges(); |
| } |
| |
| void WebPageProxy::didChangeProgress(double value) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.didChangeProgress(transaction, value); |
| |
| m_pageLoadState.commitChanges(); |
| } |
| |
| void WebPageProxy::didFinishProgress() |
| { |
| PageClientProtector protector(pageClient()); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.didFinishProgress(transaction); |
| |
| m_pageLoadState.commitChanges(); |
| } |
| |
| void WebPageProxy::setNetworkRequestsInProgress(bool networkRequestsInProgress) |
| { |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setNetworkRequestsInProgress(transaction, networkRequestsInProgress); |
| } |
| |
| void WebPageProxy::preconnectTo(const URL& url, const String& userAgent) |
| { |
| if (!m_websiteDataStore->configuration().allowsServerPreconnect()) |
| return; |
| |
| auto storedCredentialsPolicy = m_canUseCredentialStorage ? WebCore::StoredCredentialsPolicy::Use : WebCore::StoredCredentialsPolicy::DoNotUse; |
| |
| websiteDataStore().networkProcess().preconnectTo(sessionID(), identifier(), webPageID(), url, userAgent, storedCredentialsPolicy, isNavigatingToAppBoundDomain(), m_lastNavigationWasAppInitiated ? LastNavigationWasAppInitiated::Yes : LastNavigationWasAppInitiated::No); |
| } |
| |
| void WebPageProxy::setCanUseCredentialStorage(bool canUseCredentialStorage) |
| { |
| m_canUseCredentialStorage = canUseCredentialStorage; |
| send(Messages::WebPage::SetCanUseCredentialStorage(canUseCredentialStorage)); |
| } |
| |
| void WebPageProxy::didDestroyNavigation(uint64_t navigationID) |
| { |
| MESSAGE_CHECK(m_process, WebNavigationState::NavigationMap::isValidKey(navigationID)); |
| |
| PageClientProtector protector(pageClient()); |
| |
| // On process-swap, the previous process tries to destroy the navigation but the provisional process is actually taking over the navigation. |
| if (m_provisionalPage && m_provisionalPage->navigationID() == navigationID) |
| return; |
| |
| m_navigationState->didDestroyNavigation(navigationID); |
| } |
| |
| void WebPageProxy::didStartProvisionalLoadForFrame(FrameIdentifier frameID, FrameInfoData&& frameInfo, ResourceRequest&& request, uint64_t navigationID, URL&& url, URL&& unreachableURL, const UserData& userData) |
| { |
| didStartProvisionalLoadForFrameShared(m_process.copyRef(), frameID, WTFMove(frameInfo), WTFMove(request), navigationID, WTFMove(url), WTFMove(unreachableURL), userData); |
| } |
| |
| void WebPageProxy::didStartProvisionalLoadForFrameShared(Ref<WebProcessProxy>&& process, FrameIdentifier frameID, FrameInfoData&& frameInfo, ResourceRequest&& request, uint64_t navigationID, URL&& url, URL&& unreachableURL, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| RefPtr frame = process->webFrame(frameID); |
| MESSAGE_CHECK(process, frame); |
| MESSAGE_CHECK_URL(process, url); |
| |
| // If the page starts a new main frame provisional load, then cancel any pending one in a provisional process. |
| if (frame->isMainFrame() && m_provisionalPage && m_provisionalPage->mainFrame() != frame) { |
| m_provisionalPage->cancel(); |
| m_provisionalPage = nullptr; |
| } |
| |
| // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the back/forward cache. |
| RefPtr<API::Navigation> navigation; |
| if (frame->isMainFrame() && navigationID) |
| navigation = navigationState().navigation(navigationID); |
| |
| LOG(Loading, "WebPageProxy %" PRIu64 " in process pid %i didStartProvisionalLoadForFrame to frameID %" PRIu64 ", navigationID %" PRIu64 ", url %s", m_identifier.toUInt64(), process->processIdentifier(), frameID.toUInt64(), navigationID, url.string().utf8().data()); |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didStartProvisionalLoadForFrame: frameID=%" PRIu64 ", isMainFrame=%d", frameID.toUInt64(), frame->isMainFrame()); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.clearPendingAPIRequest(transaction); |
| |
| if (frame->isMainFrame()) { |
| process->didStartProvisionalLoadForMainFrame(url); |
| reportPageLoadResult(ResourceError { ResourceError::Type::Cancellation }); |
| m_pageLoadStart = MonotonicTime::now(); |
| m_pageLoadState.didStartProvisionalLoad(transaction, url.string(), unreachableURL.string()); |
| pageClient().didStartProvisionalLoadForMainFrame(); |
| closeOverlayedViews(); |
| } |
| |
| frame->setUnreachableURL(unreachableURL); |
| frame->didStartProvisionalLoad(url); |
| |
| m_pageLoadState.commitChanges(); |
| if (m_loaderClient) |
| m_loaderClient->didStartProvisionalLoadForFrame(*this, *frame, navigation.get(), process->transformHandlesToObjects(userData.object()).get()); |
| else { |
| if (frameInfo.isMainFrame) |
| m_navigationClient->didStartProvisionalNavigation(*this, request, navigation.get(), process->transformHandlesToObjects(userData.object()).get()); |
| m_navigationClient->didStartProvisionalLoadForFrame(*this, WTFMove(request), WTFMove(frameInfo)); |
| } |
| |
| #if ENABLE(WEB_AUTHN) |
| m_websiteDataStore->authenticatorManager().cancelRequest(m_webPageID, frameID); |
| #endif |
| } |
| |
| void WebPageProxy::didExplicitOpenForFrame(FrameIdentifier frameID, URL&& url, String&& mimeType) |
| { |
| auto* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| if (!checkURLReceivedFromCurrentOrPreviousWebProcess(m_process, url)) { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Process, "Ignoring WebPageProxy::DidExplicitOpenForFrame() IPC from the WebContent process because the file URL is outside the sandbox"); |
| return; |
| } |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| if (frame->isMainFrame()) |
| m_pageLoadState.didExplicitOpen(transaction, url.string()); |
| |
| frame->didExplicitOpen(WTFMove(url), WTFMove(mimeType)); |
| |
| m_hasCommittedAnyProvisionalLoads = true; |
| m_process->didCommitProvisionalLoad(); |
| |
| m_pageLoadState.commitChanges(); |
| } |
| |
| void WebPageProxy::didReceiveServerRedirectForProvisionalLoadForFrame(FrameIdentifier frameID, uint64_t navigationID, ResourceRequest&& request, const UserData& userData) |
| { |
| didReceiveServerRedirectForProvisionalLoadForFrameShared(m_process.copyRef(), frameID, navigationID, WTFMove(request), userData); |
| } |
| |
| void WebPageProxy::didReceiveServerRedirectForProvisionalLoadForFrameShared(Ref<WebProcessProxy>&& process, FrameIdentifier frameID, uint64_t navigationID, ResourceRequest&& request, const UserData& userData) |
| { |
| LOG(Loading, "WebPageProxy::didReceiveServerRedirectForProvisionalLoadForFrame to frameID %" PRIu64 ", navigationID %" PRIu64 ", url %s", frameID.toUInt64(), navigationID, request.url().string().utf8().data()); |
| |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = process->webFrame(frameID); |
| MESSAGE_CHECK(process, frame); |
| MESSAGE_CHECK_URL(process, request.url()); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didReceiveServerRedirectForProvisionalLoadForFrame: frameID=%" PRIu64 ", isMainFrame=%d", frameID.toUInt64(), frame->isMainFrame()); |
| |
| // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the back/forward cache. |
| RefPtr<API::Navigation> navigation = navigationID ? navigationState().navigation(navigationID) : nullptr; |
| if (navigation) |
| navigation->appendRedirectionURL(request.url()); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| if (frame->isMainFrame()) |
| m_pageLoadState.didReceiveServerRedirectForProvisionalLoad(transaction, request.url().string()); |
| |
| frame->didReceiveServerRedirectForProvisionalLoad(request.url()); |
| |
| m_pageLoadState.commitChanges(); |
| if (m_loaderClient) |
| m_loaderClient->didReceiveServerRedirectForProvisionalLoadForFrame(*this, *frame, frame->isMainFrame() ? navigation.get() : nullptr, process->transformHandlesToObjects(userData.object()).get()); |
| else if (frame->isMainFrame()) |
| m_navigationClient->didReceiveServerRedirectForProvisionalNavigation(*this, navigation.get(), process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| void WebPageProxy::willPerformClientRedirectForFrame(FrameIdentifier frameID, const String& url, double delay, WebCore::LockBackForwardList) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "willPerformClientRedirectForFrame: frameID=%" PRIu64 ", isMainFrame=%d", frameID.toUInt64(), frame->isMainFrame()); |
| |
| if (frame->isMainFrame()) |
| m_navigationClient->willPerformClientRedirect(*this, url, delay); |
| } |
| |
| void WebPageProxy::didCancelClientRedirectForFrame(FrameIdentifier frameID) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didCancelClientRedirectForFrame: frameID=%" PRIu64 ", isMainFrame=%d", frameID.toUInt64(), frame->isMainFrame()); |
| |
| if (frame->isMainFrame()) |
| m_navigationClient->didCancelClientRedirect(*this); |
| } |
| |
| void WebPageProxy::didChangeProvisionalURLForFrame(FrameIdentifier frameID, uint64_t navigationID, URL&& url) |
| { |
| didChangeProvisionalURLForFrameShared(m_process.copyRef(), frameID, navigationID, WTFMove(url)); |
| } |
| |
| void WebPageProxy::didChangeProvisionalURLForFrameShared(Ref<WebProcessProxy>&& process, FrameIdentifier frameID, uint64_t, URL&& url) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = process->webFrame(frameID); |
| MESSAGE_CHECK(process, frame); |
| MESSAGE_CHECK(process, frame->frameLoadState().state() == FrameLoadState::State::Provisional); |
| MESSAGE_CHECK_URL(process, url); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| // Internally, we handle this the same way we handle a server redirect. There are no client callbacks |
| // for this, but if this is the main frame, clients may observe a change to the page's URL. |
| if (frame->isMainFrame()) |
| m_pageLoadState.didReceiveServerRedirectForProvisionalLoad(transaction, url.string()); |
| |
| frame->didReceiveServerRedirectForProvisionalLoad(url); |
| } |
| |
| void WebPageProxy::didFailProvisionalLoadForFrame(FrameIdentifier frameID, FrameInfoData&& frameInfo, WebCore::ResourceRequest&& request, uint64_t navigationID, const String& provisionalURL, const ResourceError& error, WillContinueLoading willContinueLoading, const UserData& userData) |
| { |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| if (m_provisionalPage && frame->isMainFrame()) { |
| // The load did not fail, it is merely happening in a new provisional process. |
| return; |
| } |
| |
| didFailProvisionalLoadForFrameShared(m_process.copyRef(), *frame, WTFMove(frameInfo), WTFMove(request), navigationID, provisionalURL, error, willContinueLoading, userData); |
| } |
| |
| void WebPageProxy::didFailProvisionalLoadForFrameShared(Ref<WebProcessProxy>&& process, WebFrameProxy& frame, FrameInfoData&& frameInfo, WebCore::ResourceRequest&& request, uint64_t navigationID, const String& provisionalURL, const ResourceError& error, WillContinueLoading willContinueLoading, const UserData& userData) |
| { |
| LOG(Loading, "(Loading) WebPageProxy %" PRIu64 " in web process pid %i didFailProvisionalLoadForFrame to provisionalURL %s", m_identifier.toUInt64(), process->processIdentifier(), provisionalURL.utf8().data()); |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Process, "didFailProvisionalLoadForFrame: frameID=%" PRIu64 ", isMainFrame=%d, domain=%s, code=%d, isMainFrame=%d", frame.frameID().toUInt64(), frame.isMainFrame(), error.domain().utf8().data(), error.errorCode(), frame.isMainFrame()); |
| |
| PageClientProtector protector(pageClient()); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process->processPool().automationSession()) |
| automationSession->navigationOccurredForFrame(frame); |
| } |
| |
| // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the back/forward cache. |
| RefPtr<API::Navigation> navigation; |
| if (frame.isMainFrame() && navigationID) |
| navigation = navigationState().takeNavigation(navigationID); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| if (frame.isMainFrame()) { |
| reportPageLoadResult(error); |
| m_pageLoadState.didFailProvisionalLoad(transaction); |
| pageClient().didFailProvisionalLoadForMainFrame(); |
| if (navigation) |
| navigation->setClientNavigationActivity(nullptr); |
| |
| callLoadCompletionHandlersIfNecessary(false); |
| } |
| |
| frame.didFailProvisionalLoad(); |
| |
| m_pageLoadState.commitChanges(); |
| |
| ASSERT(!m_failingProvisionalLoadURL); |
| m_failingProvisionalLoadURL = provisionalURL; |
| |
| if (m_loaderClient) |
| m_loaderClient->didFailProvisionalLoadWithErrorForFrame(*this, frame, navigation.get(), error, process->transformHandlesToObjects(userData.object()).get()); |
| else { |
| m_navigationClient->didFailProvisionalNavigationWithError(*this, FrameInfoData { frameInfo }, navigation.get(), error, process->transformHandlesToObjects(userData.object()).get()); |
| m_navigationClient->didFailProvisionalLoadWithErrorForFrame(*this, WTFMove(request), error, WTFMove(frameInfo)); |
| } |
| |
| m_failingProvisionalLoadURL = { }; |
| |
| // If the provisional page's load fails then we destroy the provisional page. |
| if (m_provisionalPage && m_provisionalPage->mainFrame() == &frame && willContinueLoading == WillContinueLoading::No) |
| m_provisionalPage = nullptr; |
| } |
| |
| #if ENABLE(SERVICE_WORKER) |
| void WebPageProxy::didFinishServiceWorkerPageRegistration(bool success) |
| { |
| ASSERT(m_isServiceWorkerPage); |
| ASSERT(m_serviceWorkerLaunchCompletionHandler); |
| |
| if (m_serviceWorkerLaunchCompletionHandler) |
| m_serviceWorkerLaunchCompletionHandler(success); |
| } |
| #endif |
| |
| void WebPageProxy::callLoadCompletionHandlersIfNecessary(bool success) |
| { |
| #if ENABLE(SERVICE_WORKER) |
| if (m_isServiceWorkerPage && m_serviceWorkerLaunchCompletionHandler && !success) |
| m_serviceWorkerLaunchCompletionHandler(false); |
| |
| if (m_serviceWorkerOpenWindowCompletionCallback) |
| m_serviceWorkerOpenWindowCompletionCallback(success ? webPageID() : std::optional<WebCore::PageIdentifier> { }); |
| #endif |
| } |
| |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| static OptionSet<CrossSiteNavigationDataTransfer::Flag> checkIfNavigationContainsDataTransfer(const SecurityOriginData requesterOrigin, const ResourceRequest& currentRequest) |
| { |
| OptionSet<CrossSiteNavigationDataTransfer::Flag> navigationDataTransfer; |
| if (requesterOrigin.securityOrigin()->isUnique()) |
| return navigationDataTransfer; |
| |
| auto currentURL = currentRequest.url(); |
| if (!currentURL.query().isEmpty() || !currentURL.fragmentIdentifier().isEmpty()) |
| navigationDataTransfer.add(CrossSiteNavigationDataTransfer::Flag::DestinationLinkDecoration); |
| |
| URL referrerURL { currentRequest.httpReferrer() }; |
| if (!referrerURL.query().isEmpty() || !referrerURL.fragmentIdentifier().isEmpty()) |
| navigationDataTransfer.add(CrossSiteNavigationDataTransfer::Flag::ReferrerLinkDecoration); |
| |
| return navigationDataTransfer; |
| } |
| #endif |
| |
| void WebPageProxy::didCommitLoadForFrame(FrameIdentifier frameID, FrameInfoData&& frameInfo, ResourceRequest&& request, uint64_t navigationID, const String& mimeType, bool frameHasCustomContentProvider, WebCore::FrameLoadType frameLoadType, const WebCore::CertificateInfo& certificateInfo, bool usedLegacyTLS, bool containsPluginDocument, std::optional<HasInsecureContent> hasInsecureContent, WebCore::MouseEventPolicy mouseEventPolicy, const UserData& userData) |
| { |
| LOG(Loading, "(Loading) WebPageProxy %" PRIu64 " didCommitLoadForFrame in navigation %" PRIu64, m_identifier.toUInt64(), navigationID); |
| LOG(BackForward, "(Back/Forward) After load commit, back/forward list is now:%s", m_backForwardList->loggingString()); |
| |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didCommitLoadForFrame: frameID=%" PRIu64 ", isMainFrame=%d", frameID.toUInt64(), frame->isMainFrame()); |
| |
| // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the back/forward cache. |
| RefPtr<API::Navigation> navigation; |
| if (frame->isMainFrame() && navigationID && (navigation = navigationState().navigation(navigationID))) { |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| auto requesterOrigin = navigation->lastNavigationAction().requesterOrigin; |
| auto currentRequest = navigation->currentRequest(); |
| auto navigationDataTransfer = checkIfNavigationContainsDataTransfer(requesterOrigin, currentRequest); |
| if (!navigationDataTransfer.isEmpty()) { |
| RegistrableDomain currentDomain { currentRequest.url() }; |
| URL requesterURL { requesterOrigin.toString() }; |
| if (!currentDomain.matches(requesterURL)) |
| m_websiteDataStore->networkProcess().didCommitCrossSiteLoadWithDataTransfer(m_websiteDataStore->sessionID(), RegistrableDomain { requesterURL }, currentDomain, navigationDataTransfer, m_identifier, m_webPageID); |
| } |
| #endif |
| } |
| |
| m_hasCommittedAnyProvisionalLoads = true; |
| m_process->didCommitProvisionalLoad(); |
| |
| if (frame->isMainFrame()) { |
| m_hasUpdatedRenderingAfterDidCommitLoad = false; |
| #if PLATFORM(IOS_FAMILY) |
| m_firstLayerTreeTransactionIdAfterDidCommitLoad = downcast<RemoteLayerTreeDrawingAreaProxy>(*drawingArea()).nextLayerTreeTransactionID(); |
| #endif |
| } |
| |
| auto transaction = m_pageLoadState.transaction(); |
| Ref<WebCertificateInfo> webCertificateInfo = WebCertificateInfo::create(certificateInfo); |
| bool markPageInsecure = hasInsecureContent ? hasInsecureContent.value() == HasInsecureContent::Yes : certificateInfo.containsNonRootSHA1SignedCertificate(); |
| |
| if (frame->isMainFrame()) { |
| m_pageLoadState.didCommitLoad(transaction, webCertificateInfo, markPageInsecure, usedLegacyTLS); |
| m_shouldSuppressNextAutomaticNavigationSnapshot = false; |
| } else if (markPageInsecure) |
| m_pageLoadState.didDisplayOrRunInsecureContent(transaction); |
| |
| #if USE(APPKIT) |
| // FIXME (bug 59111): didCommitLoadForFrame comes too late when restoring a page from b/f cache, making us disable secure event mode in password fields. |
| // FIXME: A load going on in one frame shouldn't affect text editing in other frames on the page. |
| pageClient().resetSecureInputState(); |
| #endif |
| |
| frame->didCommitLoad(mimeType, webCertificateInfo, containsPluginDocument); |
| |
| if (frame->isMainFrame()) { |
| std::optional<WebCore::PrivateClickMeasurement> privateClickMeasurement; |
| if (m_privateClickMeasurement) |
| privateClickMeasurement = m_privateClickMeasurement->pcm; |
| else if (navigation && navigation->privateClickMeasurement()) |
| privateClickMeasurement = navigation->privateClickMeasurement(); |
| if (privateClickMeasurement) { |
| if (privateClickMeasurement->destinationSite().matches(frame->url()) || privateClickMeasurement->isSKAdNetworkAttribution()) |
| websiteDataStore().storePrivateClickMeasurement(*privateClickMeasurement); |
| } |
| } |
| m_privateClickMeasurement.reset(); |
| |
| if (frame->isMainFrame()) { |
| m_mainFrameHasCustomContentProvider = frameHasCustomContentProvider; |
| |
| if (m_mainFrameHasCustomContentProvider) { |
| // Always assume that the main frame is pinned here, since the custom representation view will handle |
| // any wheel events and dispatch them to the WKView when necessary. |
| m_mainFramePinnedState = { true, true, true, true }; |
| m_uiClient->pinnedStateDidChange(*this); |
| } |
| pageClient().didCommitLoadForMainFrame(mimeType, frameHasCustomContentProvider); |
| } |
| |
| // Even if WebPage has the default pageScaleFactor (and therefore doesn't reset it), |
| // WebPageProxy's cache of the value can get out of sync (e.g. in the case where a |
| // plugin is handling page scaling itself) so we should reset it to the default |
| // for standard main frame loads. |
| if (frame->isMainFrame()) { |
| m_pageScaleFactor = 1; |
| m_pluginScaleFactor = 1; |
| m_mainFramePluginHandlesPageScaleGesture = false; |
| #if ENABLE(POINTER_LOCK) |
| requestPointerUnlock(); |
| #endif |
| pageClient().setMouseEventPolicy(mouseEventPolicy); |
| #if ENABLE(UI_PROCESS_PDF_HUD) |
| pageClient().removeAllPDFHUDs(); |
| #endif |
| } |
| |
| m_pageLoadState.commitChanges(); |
| if (m_loaderClient) |
| m_loaderClient->didCommitLoadForFrame(*this, *frame, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); |
| else { |
| if (frameInfo.isMainFrame) |
| m_navigationClient->didCommitNavigation(*this, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); |
| m_navigationClient->didCommitLoadForFrame(*this, WTFMove(request), WTFMove(frameInfo)); |
| } |
| if (frame->isMainFrame()) { |
| #if ENABLE(ATTACHMENT_ELEMENT) |
| invalidateAllAttachments(); |
| #endif |
| #if ENABLE(REMOTE_INSPECTOR) |
| remoteInspectorInformationDidChange(); |
| #endif |
| #if USE(APPKIT) |
| closeSharedPreviewPanelIfNecessary(); |
| #endif |
| } |
| |
| #if ENABLE(MEDIA_SESSION_COORDINATOR) && HAVE(GROUP_ACTIVITIES) |
| if (frame->isMainFrame() && m_preferences->mediaSessionCoordinatorEnabled()) |
| GroupActivitiesSessionNotifier::sharedNotifier().webPageURLChanged(*this); |
| #endif |
| } |
| |
| void WebPageProxy::didFinishDocumentLoadForFrame(FrameIdentifier frameID, uint64_t navigationID, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didFinishDocumentLoadForFrame: frameID=%" PRIu64 ", isMainFrame=%d", frameID.toUInt64(), frame->isMainFrame()); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->documentLoadedForFrame(*frame); |
| } |
| |
| // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the back/forward cache. |
| RefPtr<API::Navigation> navigation; |
| if (frame->isMainFrame() && navigationID) |
| navigation = navigationState().navigation(navigationID); |
| |
| if (frame->isMainFrame()) { |
| m_navigationClient->didFinishDocumentLoad(*this, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| m_didFinishDocumentLoadForMainFrameTimestamp = MonotonicTime::now(); |
| #endif |
| } |
| } |
| |
| void WebPageProxy::didFinishLoadForFrame(FrameIdentifier frameID, FrameInfoData&& frameInfo, ResourceRequest&& request, uint64_t navigationID, const UserData& userData) |
| { |
| LOG(Loading, "WebPageProxy::didFinishLoadForFrame - WebPageProxy %p with navigationID %" PRIu64 " didFinishLoad", this, navigationID); |
| |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didFinishLoadForFrame: frameID=%" PRIu64 ", isMainFrame=%d", frameID.toUInt64(), frame->isMainFrame()); |
| |
| // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the back/forward cache. |
| RefPtr<API::Navigation> navigation; |
| if (frame->isMainFrame() && navigationID && navigationState().hasNavigation(navigationID)) |
| navigation = navigationState().navigation(navigationID); |
| |
| bool isMainFrame = frame->isMainFrame(); |
| if (!isMainFrame || !navigationID || navigation) { |
| auto transaction = m_pageLoadState.transaction(); |
| |
| if (isMainFrame) |
| m_pageLoadState.didFinishLoad(transaction); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->navigationOccurredForFrame(*frame); |
| } |
| |
| frame->didFinishLoad(); |
| |
| m_pageLoadState.commitChanges(); |
| } |
| |
| if (m_loaderClient) |
| m_loaderClient->didFinishLoadForFrame(*this, *frame, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); |
| else { |
| if (frameInfo.isMainFrame) |
| m_navigationClient->didFinishNavigation(*this, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); |
| m_navigationClient->didFinishLoadForFrame(*this, WTFMove(request), WTFMove(frameInfo)); |
| } |
| |
| if (isMainFrame) { |
| reportPageLoadResult(); |
| pageClient().didFinishNavigation(navigation.get()); |
| |
| if (navigation) |
| navigation->setClientNavigationActivity(nullptr); |
| |
| resetRecentCrashCountSoon(); |
| |
| notifyProcessPoolToPrewarm(); |
| |
| callLoadCompletionHandlersIfNecessary(true); |
| } |
| |
| m_isLoadingAlternateHTMLStringForFailingProvisionalLoad = false; |
| } |
| |
| void WebPageProxy::didFailLoadForFrame(FrameIdentifier frameID, FrameInfoData&& frameInfo, ResourceRequest&& request, uint64_t navigationID, const ResourceError& error, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Loading, "didFailLoadForFrame: frameID=%" PRIu64 ", isMainFrame=%d, domain=%s, code=%d", frameID.toUInt64(), frame->isMainFrame(), error.domain().utf8().data(), error.errorCode()); |
| |
| // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the back/forward cache. |
| RefPtr<API::Navigation> navigation; |
| if (frame->isMainFrame() && navigationID) |
| navigation = navigationState().navigation(navigationID); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| bool isMainFrame = frame->isMainFrame(); |
| |
| if (isMainFrame) |
| m_pageLoadState.didFailLoad(transaction); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->navigationOccurredForFrame(*frame); |
| } |
| |
| frame->didFailLoad(); |
| |
| m_pageLoadState.commitChanges(); |
| if (m_loaderClient) |
| m_loaderClient->didFailLoadWithErrorForFrame(*this, *frame, navigation.get(), error, m_process->transformHandlesToObjects(userData.object()).get()); |
| else { |
| if (frameInfo.isMainFrame) |
| m_navigationClient->didFailNavigationWithError(*this, frameInfo, navigation.get(), error, m_process->transformHandlesToObjects(userData.object()).get()); |
| m_navigationClient->didFailLoadWithErrorForFrame(*this, WTFMove(request), error, WTFMove(frameInfo)); |
| } |
| |
| if (isMainFrame) { |
| reportPageLoadResult(error); |
| pageClient().didFailNavigation(navigation.get()); |
| if (navigation) |
| navigation->setClientNavigationActivity(nullptr); |
| |
| callLoadCompletionHandlersIfNecessary(false); |
| } |
| } |
| |
| void WebPageProxy::didSameDocumentNavigationForFrame(FrameIdentifier frameID, uint64_t navigationID, uint32_t opaqueSameDocumentNavigationType, URL&& url, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| MESSAGE_CHECK_URL(m_process, url); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didSameDocumentNavigationForFrame: frameID=%" PRIu64 ", isMainFrame=%d", frameID.toUInt64(), frame->isMainFrame()); |
| |
| // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the back/forward cache. |
| RefPtr<API::Navigation> navigation; |
| if (frame->isMainFrame() && navigationID) |
| navigation = navigationState().navigation(navigationID); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| bool isMainFrame = frame->isMainFrame(); |
| if (isMainFrame) |
| m_pageLoadState.didSameDocumentNavigation(transaction, url.string()); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->navigationOccurredForFrame(*frame); |
| } |
| |
| m_pageLoadState.clearPendingAPIRequest(transaction); |
| frame->didSameDocumentNavigation(url); |
| |
| m_pageLoadState.commitChanges(); |
| |
| SameDocumentNavigationType navigationType = static_cast<SameDocumentNavigationType>(opaqueSameDocumentNavigationType); |
| if (isMainFrame) |
| m_navigationClient->didSameDocumentNavigation(*this, navigation.get(), navigationType, m_process->transformHandlesToObjects(userData.object()).get()); |
| |
| if (isMainFrame) |
| pageClient().didSameDocumentNavigationForMainFrame(navigationType); |
| } |
| |
| void WebPageProxy::didSameDocumentNavigationForFrameViaJSHistoryAPI(WebCore::FrameIdentifier frameID, uint32_t opaqueSameDocumentNavigationType, URL url, NavigationActionData&& navigationActionData, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| MESSAGE_CHECK_URL(m_process, url); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didSameDocumentNavigationForFrameViaJSHistoryAPI: frameID=%" PRIu64 ", isMainFrame=%d, type=%u", frameID.toUInt64(), frame->isMainFrame(), opaqueSameDocumentNavigationType); |
| |
| // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the back/forward cache. |
| RefPtr<API::Navigation> navigation; |
| if (frame->isMainFrame()) { |
| navigation = navigationState().createLoadRequestNavigation(ResourceRequest(url), m_backForwardList->currentItem()); |
| navigation->setLastNavigationAction(WTFMove(navigationActionData)); |
| } |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| bool isMainFrame = frame->isMainFrame(); |
| if (isMainFrame) |
| m_pageLoadState.didSameDocumentNavigation(transaction, url.string()); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->navigationOccurredForFrame(*frame); |
| } |
| |
| m_pageLoadState.clearPendingAPIRequest(transaction); |
| frame->didSameDocumentNavigation(url); |
| |
| m_pageLoadState.commitChanges(); |
| |
| SameDocumentNavigationType navigationType = static_cast<SameDocumentNavigationType>(opaqueSameDocumentNavigationType); |
| if (isMainFrame) |
| m_navigationClient->didSameDocumentNavigation(*this, navigation.get(), navigationType, m_process->transformHandlesToObjects(userData.object()).get()); |
| |
| if (isMainFrame) |
| pageClient().didSameDocumentNavigationForMainFrame(navigationType); |
| |
| if (navigation) |
| navigationState().didDestroyNavigation(navigation->navigationID()); |
| } |
| |
| void WebPageProxy::didChangeMainDocument(FrameIdentifier frameID) |
| { |
| #if ENABLE(MEDIA_STREAM) |
| if (m_userMediaPermissionRequestManager) { |
| m_userMediaPermissionRequestManager->resetAccess(frameID); |
| |
| #if ENABLE(GPU_PROCESS) |
| if (auto* gpuProcess = m_process->processPool().gpuProcess()) { |
| if (auto* frame = m_process->webFrame(frameID)) |
| gpuProcess->updateCaptureOrigin(SecurityOriginData::fromURL(frame->url()), m_process->coreProcessIdentifier()); |
| } |
| #endif |
| } |
| |
| #else |
| UNUSED_PARAM(frameID); |
| #endif |
| |
| m_isQuotaIncreaseDenied = false; |
| |
| m_speechRecognitionPermissionManager = nullptr; |
| } |
| |
| void WebPageProxy::viewIsBecomingVisible() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(ViewState, "viewIsBecomingVisible:"); |
| m_process->markProcessAsRecentlyUsed(); |
| #if ENABLE(MEDIA_STREAM) |
| if (m_userMediaPermissionRequestManager) |
| m_userMediaPermissionRequestManager->viewIsBecomingVisible(); |
| #endif |
| } |
| |
| void WebPageProxy::didReceiveTitleForFrame(FrameIdentifier frameID, const String& title, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| if (frame->isMainFrame()) |
| m_pageLoadState.setTitle(transaction, title); |
| |
| frame->didChangeTitle(title); |
| |
| m_pageLoadState.commitChanges(); |
| |
| #if ENABLE(REMOTE_INSPECTOR) |
| if (frame->isMainFrame()) |
| remoteInspectorInformationDidChange(); |
| #endif |
| } |
| |
| void WebPageProxy::didFirstLayoutForFrame(FrameIdentifier, const UserData& userData) |
| { |
| } |
| |
| void WebPageProxy::didFirstVisuallyNonEmptyLayoutForFrame(FrameIdentifier frameID, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| if (m_loaderClient) |
| m_loaderClient->didFirstVisuallyNonEmptyLayoutForFrame(*this, *frame, m_process->transformHandlesToObjects(userData.object()).get()); |
| |
| if (frame->isMainFrame()) |
| pageClient().didFirstVisuallyNonEmptyLayoutForMainFrame(); |
| } |
| |
| void WebPageProxy::didLayoutForCustomContentProvider() |
| { |
| didReachLayoutMilestone({ DidFirstLayout, DidFirstVisuallyNonEmptyLayout, DidHitRelevantRepaintedObjectsAreaThreshold }); |
| } |
| |
| void WebPageProxy::didReachLayoutMilestone(OptionSet<WebCore::LayoutMilestone> layoutMilestones) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| if (layoutMilestones.contains(DidFirstVisuallyNonEmptyLayout)) |
| pageClient().clearSafeBrowsingWarningIfForMainFrameNavigation(); |
| |
| if (m_loaderClient) |
| m_loaderClient->didReachLayoutMilestone(*this, layoutMilestones); |
| m_navigationClient->renderingProgressDidChange(*this, layoutMilestones); |
| } |
| |
| void WebPageProxy::didDisplayInsecureContentForFrame(FrameIdentifier frameID, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.didDisplayOrRunInsecureContent(transaction); |
| m_pageLoadState.commitChanges(); |
| |
| m_navigationClient->didDisplayInsecureContent(*this, m_process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| void WebPageProxy::didRunInsecureContentForFrame(FrameIdentifier frameID, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.didDisplayOrRunInsecureContent(transaction); |
| m_pageLoadState.commitChanges(); |
| |
| m_navigationClient->didRunInsecureContent(*this, m_process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| void WebPageProxy::didDetectXSSForFrame(FrameIdentifier, const UserData&) |
| { |
| } |
| |
| void WebPageProxy::mainFramePluginHandlesPageScaleGestureDidChange(bool mainFramePluginHandlesPageScaleGesture) |
| { |
| m_mainFramePluginHandlesPageScaleGesture = mainFramePluginHandlesPageScaleGesture; |
| } |
| |
| #if !PLATFORM(COCOA) |
| void WebPageProxy::beginSafeBrowsingCheck(const URL&, bool, WebFramePolicyListenerProxy& listener) |
| { |
| listener.didReceiveSafeBrowsingResults({ }); |
| } |
| #endif |
| |
| void WebPageProxy::decidePolicyForNavigationActionAsync(FrameIdentifier frameID, FrameInfoData&& frameInfo, PolicyCheckIdentifier identifier, uint64_t navigationID, |
| NavigationActionData&& navigationActionData, FrameInfoData&& originatingFrameInfo, std::optional<WebPageProxyIdentifier> originatingPageID, const WebCore::ResourceRequest& originalRequest, WebCore::ResourceRequest&& request, |
| IPC::FormDataReference&& requestBody, WebCore::ResourceResponse&& redirectResponse, const UserData& userData, uint64_t listenerID) |
| { |
| decidePolicyForNavigationActionAsyncShared(m_process.copyRef(), m_webPageID, frameID, WTFMove(frameInfo), identifier, navigationID, WTFMove(navigationActionData), WTFMove(originatingFrameInfo), originatingPageID, originalRequest, WTFMove(request), WTFMove(requestBody), WTFMove(redirectResponse), userData, listenerID); |
| } |
| |
| void WebPageProxy::decidePolicyForNavigationActionAsyncShared(Ref<WebProcessProxy>&& process, PageIdentifier webPageID, FrameIdentifier frameID, FrameInfoData&& frameInfo, |
| WebCore::PolicyCheckIdentifier identifier, uint64_t navigationID, NavigationActionData&& navigationActionData, FrameInfoData&& originatingFrameInfo, std::optional<WebPageProxyIdentifier> originatingPageID, |
| const WebCore::ResourceRequest& originalRequest, WebCore::ResourceRequest&& request, IPC::FormDataReference&& requestBody, WebCore::ResourceResponse&& redirectResponse, |
| const UserData& userData, uint64_t listenerID) |
| { |
| auto* frame = process->webFrame(frameID); |
| MESSAGE_CHECK(process, frame); |
| |
| auto sender = PolicyDecisionSender::create(identifier, [webPageID, frameID, listenerID, process] (const auto& policyDecision) { |
| process->send(Messages::WebPage::DidReceivePolicyDecision(frameID, listenerID, policyDecision |
| #if !ENABLE(CONTENT_FILTERING_IN_NETWORKING_PROCESS) |
| , createNetworkExtensionsSandboxExtensions(process) |
| #endif |
| ), webPageID); |
| }); |
| |
| decidePolicyForNavigationAction(process.copyRef(), webPageID, *frame, WTFMove(frameInfo), navigationID, WTFMove(navigationActionData), WTFMove(originatingFrameInfo), originatingPageID, |
| originalRequest, WTFMove(request), WTFMove(requestBody), WTFMove(redirectResponse), userData, WTFMove(sender)); |
| } |
| |
| #if PLATFORM(COCOA) |
| // https://html.spec.whatwg.org/#hand-off-to-external-software |
| static bool frameSandboxAllowsOpeningExternalCustomProtocols(SandboxFlags sandboxFlags, bool hasUserGesture) |
| { |
| if (!(sandboxFlags & SandboxPopups) || !(sandboxFlags & SandboxTopNavigation) || !(sandboxFlags & SandboxTopNavigationToCustomProtocols)) |
| return true; |
| |
| return !(sandboxFlags & SandboxTopNavigationByUserActivation) && hasUserGesture; |
| } |
| #endif |
| |
| void WebPageProxy::decidePolicyForNavigationAction(Ref<WebProcessProxy>&& process, PageIdentifier webPageID, WebFrameProxy& frame, FrameInfoData&& frameInfo, uint64_t navigationID, |
| NavigationActionData&& navigationActionData, FrameInfoData&& originatingFrameInfoData, std::optional<WebPageProxyIdentifier> originatingPageID, const WebCore::ResourceRequest& originalRequest, WebCore::ResourceRequest&& request, |
| IPC::FormDataReference&& requestBody, WebCore::ResourceResponse&& redirectResponse, const UserData& userData, Ref<PolicyDecisionSender>&& sender) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "decidePolicyForNavigationAction: frameID=%llu, isMainFrame=%d, navigationID=%llu", frame.frameID().toUInt64(), frame.isMainFrame(), navigationID); |
| |
| LOG(Loading, "WebPageProxy::decidePolicyForNavigationAction - Original URL %s, current target URL %s", originalRequest.url().string().utf8().data(), request.url().string().utf8().data()); |
| |
| PageClientProtector protector(pageClient()); |
| |
| // Make the request whole again as we do not normally encode the request's body when sending it over IPC, for performance reasons. |
| request.setHTTPBody(requestBody.takeData()); |
| |
| auto transaction = m_pageLoadState.transaction(); |
| |
| bool fromAPI = request.url() == m_pageLoadState.pendingAPIRequestURL(); |
| if (navigationID && !fromAPI) |
| m_pageLoadState.clearPendingAPIRequest(transaction); |
| |
| if (!checkURLReceivedFromCurrentOrPreviousWebProcess(process, request.url())) { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Process, "Ignoring request to load this main resource because it is outside the sandbox"); |
| sender->send(PolicyDecision { sender->identifier(), isNavigatingToAppBoundDomain(), PolicyAction::Ignore, 0, std::nullopt, std::nullopt }); |
| return; |
| } |
| |
| MESSAGE_CHECK_URL(process, originalRequest.url()); |
| |
| RefPtr<API::Navigation> navigation; |
| if (navigationID) |
| navigation = m_navigationState->navigation(navigationID); |
| |
| // When process-swapping on a redirect, the navigationActionData / originatingFrameInfoData provided by the fresh new WebProcess are inaccurate since |
| // the new process does not have sufficient information. To address the issue, we restore the information we stored on the NavigationAction during the original request |
| // policy decision. |
| if (navigationActionData.isRedirect && navigation) { |
| navigationActionData = navigation->lastNavigationAction(); |
| navigationActionData.isRedirect = true; |
| originatingFrameInfoData = navigation->originatingFrameInfo(); |
| frameInfo.securityOrigin = navigation->destinationFrameSecurityOrigin(); |
| } |
| |
| if (!navigation) { |
| if (auto targetBackForwardItemIdentifier = navigationActionData.targetBackForwardItemIdentifier) { |
| if (auto* item = m_backForwardList->itemForID(*targetBackForwardItemIdentifier)) { |
| auto* fromItem = navigationActionData.sourceBackForwardItemIdentifier ? m_backForwardList->itemForID(*navigationActionData.sourceBackForwardItemIdentifier) : nullptr; |
| if (!fromItem) |
| fromItem = m_backForwardList->currentItem(); |
| navigation = m_navigationState->createBackForwardNavigation(*item, fromItem, FrameLoadType::IndexedBackForward); |
| } |
| } |
| if (!navigation) |
| navigation = m_navigationState->createLoadRequestNavigation(ResourceRequest(request), m_backForwardList->currentItem()); |
| } |
| |
| navigationID = navigation->navigationID(); |
| |
| // Make sure the provisional page always has the latest navigationID. |
| if (m_provisionalPage && &m_provisionalPage->process() == process.ptr()) |
| m_provisionalPage->setNavigationID(navigationID); |
| |
| navigation->setCurrentRequest(ResourceRequest(request), process->coreProcessIdentifier()); |
| navigation->setLastNavigationAction(navigationActionData); |
| navigation->setOriginatingFrameInfo(originatingFrameInfoData); |
| navigation->setDestinationFrameSecurityOrigin(frameInfo.securityOrigin); |
| |
| API::Navigation* mainFrameNavigation = frame.isMainFrame() ? navigation.get() : nullptr; |
| WebFrameProxy* originatingFrame = originatingFrameInfoData.frameID ? process->webFrame(*originatingFrameInfoData.frameID) : nullptr; |
| auto destinationFrameInfo = API::FrameInfo::create(FrameInfoData { frameInfo }, this); |
| RefPtr<API::FrameInfo> sourceFrameInfo; |
| if (!fromAPI && originatingFrame == &frame) |
| sourceFrameInfo = destinationFrameInfo.copyRef(); |
| else if (!fromAPI) { |
| auto* originatingPage = originatingPageID ? process->webPage(*originatingPageID) : nullptr; |
| sourceFrameInfo = API::FrameInfo::create(WTFMove(originatingFrameInfoData), originatingPage); |
| } |
| |
| bool shouldOpenAppLinks = !m_shouldSuppressAppLinksInNextNavigationPolicyDecision |
| && destinationFrameInfo->isMainFrame() |
| && (m_mainFrame && m_mainFrame->url().host() != request.url().host()) |
| && navigationActionData.navigationType != WebCore::NavigationType::BackForward; |
| |
| auto userInitiatedActivity = process->userInitiatedActivity(navigationActionData.userGestureTokenIdentifier); |
| auto navigationAction = API::NavigationAction::create(WTFMove(navigationActionData), sourceFrameInfo.get(), destinationFrameInfo.ptr(), std::nullopt, ResourceRequest(request), originalRequest.url(), shouldOpenAppLinks, WTFMove(userInitiatedActivity), mainFrameNavigation); |
| |
| #if ENABLE(CONTENT_FILTERING) |
| if (frame.didHandleContentFilterUnblockNavigation(request)) { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Process, "Ignoring request to load this main resource because it was handled by content filter"); |
| return receivedPolicyDecision(PolicyAction::Ignore, m_navigationState->navigation(navigationID), nullptr, WTFMove(navigationAction), WTFMove(sender)); |
| } |
| #endif |
| |
| // Other ports do not implement WebPage::platformCanHandleRequest(). |
| #if PLATFORM(COCOA) |
| // Sandboxed iframes should be allowed to open external apps via custom protocols unless explicitely allowed (https://html.spec.whatwg.org/#hand-off-to-external-software). |
| bool canHandleRequest = navigationActionData.canHandleRequest || m_urlSchemeHandlersByScheme.contains<StringViewHashTranslator>(request.url().protocol()); |
| if (!canHandleRequest && !destinationFrameInfo->isMainFrame() && !frameSandboxAllowsOpeningExternalCustomProtocols(navigationActionData.effectiveSandboxFlags, !!navigationActionData.userGestureTokenIdentifier)) { |
| if (!sourceFrameInfo || !m_preferences->needsSiteSpecificQuirks() || !Quirks::shouldAllowNavigationToCustomProtocolWithoutUserGesture(request.url().protocol(), sourceFrameInfo->securityOrigin())) { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Process, "Ignoring request to load this main resource because it has a custom protocol and comes from a sandboxed iframe"); |
| process->send(Messages::WebPage::AddConsoleMessage(frame.frameID(), MessageSource::Security, MessageLevel::Error, "Ignoring request to load this main resource because it has a custom protocol and comes from a sandboxed iframe"_s, std::nullopt), webPageID); |
| return receivedPolicyDecision(PolicyAction::Ignore, m_navigationState->navigation(navigationID), nullptr, WTFMove(navigationAction), WTFMove(sender)); |
| } |
| process->send(Messages::WebPage::AddConsoleMessage(frame.frameID(), MessageSource::Security, MessageLevel::Warning, "In the future, requests to navigate to a URL with custom protocol from a sandboxed iframe will be ignored"_s, std::nullopt), webPageID); |
| } |
| #endif |
| |
| ShouldExpectSafeBrowsingResult shouldExpectSafeBrowsingResult = ShouldExpectSafeBrowsingResult::Yes; |
| if (!m_preferences->safeBrowsingEnabled()) |
| shouldExpectSafeBrowsingResult = ShouldExpectSafeBrowsingResult::No; |
| |
| ShouldExpectAppBoundDomainResult shouldExpectAppBoundDomainResult = ShouldExpectAppBoundDomainResult::No; |
| #if ENABLE(APP_BOUND_DOMAINS) |
| shouldExpectAppBoundDomainResult = ShouldExpectAppBoundDomainResult::Yes; |
| #endif |
| |
| Ref listener = frame.setUpPolicyListenerProxy([this, protectedThis = Ref { *this }, frame = Ref { frame }, sender = WTFMove(sender), navigation, navigationAction, frameInfo, userDataObject = process->transformHandlesToObjects(userData.object()).get()] (PolicyAction policyAction, API::WebsitePolicies* policies, ProcessSwapRequestedByClient processSwapRequestedByClient, RefPtr<SafeBrowsingWarning>&& safeBrowsingWarning, std::optional<NavigatingToAppBoundDomain> isAppBoundDomain) mutable { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "decidePolicyForNavigationAction: listener called: frameID=%llu, isMainFrame=%d, navigationID=%llu, policyAction=%u, safeBrowsingWarning=%d, isAppBoundDomain=%d", frame->frameID().toUInt64(), frame->isMainFrame(), navigation ? navigation->navigationID() : 0, (unsigned)policyAction, !!safeBrowsingWarning, !!isAppBoundDomain); |
| |
| navigation->setWebsitePolicies(WTFMove(policies)); |
| auto completionHandler = [this, protectedThis, frame, frameInfo, sender = WTFMove(sender), navigation, navigationAction = WTFMove(navigationAction), processSwapRequestedByClient] (PolicyAction policyAction) mutable { |
| if (frame->isMainFrame()) { |
| if (!navigation->websitePolicies()) { |
| if (auto* defaultPolicies = m_configuration->defaultWebsitePolicies()) |
| navigation->setWebsitePolicies(defaultPolicies->copy()); |
| } |
| if (auto* policies = navigation->websitePolicies()) |
| navigation->setEffectiveContentMode(effectiveContentModeAfterAdjustingPolicies(*policies, navigation->currentRequest())); |
| } |
| receivedNavigationPolicyDecision(policyAction, navigation.get(), WTFMove(navigationAction), processSwapRequestedByClient, frame, frameInfo, WTFMove(sender)); |
| }; |
| |
| #if ENABLE(APP_BOUND_DOMAINS) |
| if (policyAction != PolicyAction::Ignore) { |
| if (!setIsNavigatingToAppBoundDomainAndCheckIfPermitted(frame->isMainFrame(), navigation->currentRequest().url(), isAppBoundDomain)) { |
| auto error = errorForUnpermittedAppBoundDomainNavigation(navigation->currentRequest().url()); |
| m_navigationClient->didFailProvisionalNavigationWithError(*this, FrameInfoData { frameInfo }, navigation.get(), error, userDataObject); |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Loading, "Ignoring request to load this main resource because it is attempting to navigate away from an app-bound domain or navigate after using restricted APIs"); |
| completionHandler(PolicyAction::Ignore); |
| return; |
| } |
| if (frame->isMainFrame()) |
| m_isTopFrameNavigatingToAppBoundDomain = m_isNavigatingToAppBoundDomain; |
| } |
| #endif |
| |
| if (!m_pageClient) |
| return completionHandler(policyAction); |
| |
| m_pageClient->clearSafeBrowsingWarning(); |
| |
| if (safeBrowsingWarning) { |
| if (frame->isMainFrame() && safeBrowsingWarning->url().isValid()) { |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setPendingAPIRequest(transaction, { navigation->navigationID(), safeBrowsingWarning->url().string() }); |
| m_pageLoadState.commitChanges(); |
| } |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setTitleFromSafeBrowsingWarning(transaction, safeBrowsingWarning->title()); |
| |
| m_pageClient->showSafeBrowsingWarning(*safeBrowsingWarning, [this, protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), policyAction] (auto&& result) mutable { |
| |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.setTitleFromSafeBrowsingWarning(transaction, { }); |
| |
| switchOn(result, [&] (const URL& url) { |
| completionHandler(PolicyAction::Ignore); |
| loadRequest({ url }); |
| }, [&] (ContinueUnsafeLoad continueUnsafeLoad) { |
| switch (continueUnsafeLoad) { |
| case ContinueUnsafeLoad::No: |
| if (!hasCommittedAnyProvisionalLoads()) |
| m_uiClient->close(protectedThis.ptr()); |
| completionHandler(PolicyAction::Ignore); |
| break; |
| case ContinueUnsafeLoad::Yes: |
| completionHandler(policyAction); |
| break; |
| } |
| }); |
| }); |
| m_uiClient->didShowSafeBrowsingWarning(); |
| return; |
| } |
| completionHandler(policyAction); |
| |
| }, shouldExpectSafeBrowsingResult, shouldExpectAppBoundDomainResult); |
| if (shouldExpectSafeBrowsingResult == ShouldExpectSafeBrowsingResult::Yes) |
| beginSafeBrowsingCheck(request.url(), frame.isMainFrame(), listener); |
| #if ENABLE(APP_BOUND_DOMAINS) |
| bool shouldSendSecurityOriginData = !frame.isMainFrame() && shouldTreatURLProtocolAsAppBound(request.url(), websiteDataStore().configuration().enableInAppBrowserPrivacyForTesting()); |
| auto host = shouldSendSecurityOriginData ? frameInfo.securityOrigin.host : request.url().host(); |
| auto protocol = shouldSendSecurityOriginData ? frameInfo.securityOrigin.protocol : request.url().protocol(); |
| m_websiteDataStore->beginAppBoundDomainCheck(host.toString(), protocol.toString(), listener); |
| #endif |
| |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| auto wasPotentiallyInitiatedByUser = navigation->isLoadedWithNavigationShared() || userInitiatedActivity; |
| if (!sessionID().isEphemeral()) |
| logFrameNavigation(frame, URL { m_pageLoadState.url() }, request, redirectResponse.url(), wasPotentiallyInitiatedByUser); |
| #endif |
| |
| if (m_policyClient) |
| m_policyClient->decidePolicyForNavigationAction(*this, &frame, WTFMove(navigationAction), originatingFrame, originalRequest, WTFMove(request), WTFMove(listener), process->transformHandlesToObjects(userData.object()).get()); |
| else { |
| #if HAVE(APP_SSO) |
| if (m_shouldSuppressSOAuthorizationInNextNavigationPolicyDecision || !m_preferences->isExtensibleSSOEnabled()) |
| navigationAction->unsetShouldPerformSOAuthorization(); |
| #endif |
| |
| m_navigationClient->decidePolicyForNavigationAction(*this, WTFMove(navigationAction), WTFMove(listener), process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| m_shouldSuppressAppLinksInNextNavigationPolicyDecision = false; |
| |
| #if HAVE(APP_SSO) |
| m_shouldSuppressSOAuthorizationInNextNavigationPolicyDecision = false; |
| #endif |
| } |
| |
| WebPageProxy* WebPageProxy::nonEphemeralWebPageProxy() |
| { |
| auto processPools = WebProcessPool::allProcessPools(); |
| if (processPools.isEmpty()) |
| return nullptr; |
| |
| for (auto& webProcess : processPools[0]->processes()) { |
| for (auto& page : webProcess->pages()) { |
| if (page->sessionID().isEphemeral()) |
| continue; |
| return page; |
| } |
| } |
| return nullptr; |
| } |
| |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| void WebPageProxy::logFrameNavigation(const WebFrameProxy& frame, const URL& pageURL, const WebCore::ResourceRequest& request, const URL& redirectURL, bool wasPotentiallyInitiatedByUser) |
| { |
| ASSERT(RunLoop::isMain()); |
| |
| auto sourceURL = redirectURL; |
| bool isRedirect = !redirectURL.isNull(); |
| if (!isRedirect) { |
| sourceURL = frame.url(); |
| if (sourceURL.isNull()) |
| sourceURL = pageURL; |
| } |
| |
| auto& targetURL = request.url(); |
| |
| if (!targetURL.isValid() || !pageURL.isValid()) |
| return; |
| |
| auto targetHost = targetURL.host(); |
| auto mainFrameHost = pageURL.host(); |
| |
| if (targetHost.isEmpty() || mainFrameHost.isEmpty() || targetHost == sourceURL.host()) |
| return; |
| |
| websiteDataStore().networkProcess().send(Messages::NetworkProcess::LogFrameNavigation(m_websiteDataStore->sessionID(), RegistrableDomain { targetURL }, RegistrableDomain { pageURL }, RegistrableDomain { sourceURL }, isRedirect, frame.isMainFrame(), MonotonicTime::now() - m_didFinishDocumentLoadForMainFrameTimestamp, wasPotentiallyInitiatedByUser), 0); |
| } |
| #endif |
| |
| void WebPageProxy::decidePolicyForNavigationActionSync(FrameIdentifier frameID, bool isMainFrame, FrameInfoData&& frameInfo, PolicyCheckIdentifier identifier, |
| uint64_t navigationID, NavigationActionData&& navigationActionData, FrameInfoData&& originatingFrameInfo, std::optional<WebPageProxyIdentifier> originatingPageID, |
| const WebCore::ResourceRequest& originalRequest, WebCore::ResourceRequest&& request, IPC::FormDataReference&& requestBody, WebCore::ResourceResponse&& redirectResponse, |
| const UserData& userData, Messages::WebPageProxy::DecidePolicyForNavigationActionSync::DelayedReply&& reply) |
| { |
| auto* frame = m_process->webFrame(frameID); |
| if (!frame) { |
| // This synchronous IPC message was processed before the asynchronous DidCreateMainFrame / DidCreateSubframe one so we do not know about this frameID yet. |
| if (isMainFrame) |
| didCreateMainFrame(frameID); |
| else |
| didCreateSubframe(frameID); |
| } |
| |
| decidePolicyForNavigationActionSyncShared(m_process.copyRef(), m_webPageID, frameID, isMainFrame, WTFMove(frameInfo), identifier, navigationID, WTFMove(navigationActionData), WTFMove(originatingFrameInfo), originatingPageID, originalRequest, WTFMove(request), WTFMove(requestBody), WTFMove(redirectResponse), userData, WTFMove(reply)); |
| } |
| |
| void WebPageProxy::decidePolicyForNavigationActionSyncShared(Ref<WebProcessProxy>&& process, PageIdentifier webPageID, FrameIdentifier frameID, bool isMainFrame, FrameInfoData&& frameInfo, PolicyCheckIdentifier identifier, uint64_t navigationID, NavigationActionData&& navigationActionData, FrameInfoData&& originatingFrameInfo, std::optional<WebPageProxyIdentifier> originatingPageID, const WebCore::ResourceRequest& originalRequest, WebCore::ResourceRequest&& request, IPC::FormDataReference&& requestBody, WebCore::ResourceResponse&& redirectResponse, const UserData& userData, Messages::WebPageProxy::DecidePolicyForNavigationActionSync::DelayedReply&& reply) |
| { |
| auto sender = PolicyDecisionSender::create(identifier, WTFMove(reply)); |
| |
| auto* frame = process->webFrame(frameID); |
| MESSAGE_CHECK(process, frame); |
| |
| decidePolicyForNavigationAction(WTFMove(process), webPageID, *frame, WTFMove(frameInfo), navigationID, WTFMove(navigationActionData), WTFMove(originatingFrameInfo), originatingPageID, originalRequest, WTFMove(request), WTFMove(requestBody), WTFMove(redirectResponse), userData, sender.copyRef()); |
| |
| // If the client did not respond synchronously, proceed with the load. |
| sender->send(PolicyDecision { sender->identifier(), isNavigatingToAppBoundDomain(), PolicyAction::Use, navigationID, std::nullopt, std::nullopt }); |
| } |
| |
| void WebPageProxy::decidePolicyForNewWindowAction(FrameIdentifier frameID, FrameInfoData&& frameInfo, PolicyCheckIdentifier identifier, NavigationActionData&& navigationActionData, ResourceRequest&& request, const String& frameName, uint64_t listenerID, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| MESSAGE_CHECK_URL(m_process, request.url()); |
| |
| RefPtr<API::FrameInfo> sourceFrameInfo; |
| if (frame) |
| sourceFrameInfo = API::FrameInfo::create(WTFMove(frameInfo), this); |
| |
| auto userInitiatedActivity = m_process->userInitiatedActivity(navigationActionData.userGestureTokenIdentifier); |
| bool shouldOpenAppLinks = m_mainFrame && m_mainFrame->url().host() != request.url().host(); |
| auto navigationAction = API::NavigationAction::create(WTFMove(navigationActionData), sourceFrameInfo.get(), nullptr, frameName, ResourceRequest(request), URL { }, shouldOpenAppLinks, WTFMove(userInitiatedActivity)); |
| |
| Ref listener = frame->setUpPolicyListenerProxy([this, protectedThis = Ref { *this }, identifier, listenerID, frameID, navigationAction] (PolicyAction policyAction, API::WebsitePolicies*, ProcessSwapRequestedByClient processSwapRequestedByClient, RefPtr<SafeBrowsingWarning>&& safeBrowsingWarning, std::optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain) mutable { |
| // FIXME: Assert the API::WebsitePolicies* is nullptr here once clients of WKFramePolicyListenerUseWithPolicies go away. |
| RELEASE_ASSERT(processSwapRequestedByClient == ProcessSwapRequestedByClient::No); |
| ASSERT_UNUSED(safeBrowsingWarning, !safeBrowsingWarning); |
| |
| auto sender = PolicyDecisionSender::create(identifier, [this, protectedThis = WTFMove(protectedThis), frameID, listenerID] (const auto& policyDecision) { |
| send(Messages::WebPage::DidReceivePolicyDecision(frameID, listenerID, policyDecision |
| #if !ENABLE(CONTENT_FILTERING_IN_NETWORKING_PROCESS) |
| , createNetworkExtensionsSandboxExtensions(m_process) |
| #endif |
| )); |
| }); |
| |
| receivedPolicyDecision(policyAction, nullptr, nullptr, WTFMove(navigationAction), WTFMove(sender)); |
| }, ShouldExpectSafeBrowsingResult::No, ShouldExpectAppBoundDomainResult::No); |
| |
| if (m_policyClient) |
| m_policyClient->decidePolicyForNewWindowAction(*this, *frame, navigationAction.get(), request, frameName, WTFMove(listener), m_process->transformHandlesToObjects(userData.object()).get()); |
| else |
| m_navigationClient->decidePolicyForNavigationAction(*this, navigationAction.get(), WTFMove(listener), m_process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| void WebPageProxy::decidePolicyForResponse(FrameIdentifier frameID, FrameInfoData&& frameInfo, PolicyCheckIdentifier identifier, |
| uint64_t navigationID, const ResourceResponse& response, const ResourceRequest& request, bool canShowMIMEType, const String& downloadAttribute, |
| bool wasAllowedByInjectedBundle, uint64_t listenerID, const UserData& userData) |
| { |
| decidePolicyForResponseShared(m_process.copyRef(), m_webPageID, frameID, WTFMove(frameInfo), identifier, navigationID, response, request, canShowMIMEType, downloadAttribute, wasAllowedByInjectedBundle, listenerID, userData); |
| } |
| |
| void WebPageProxy::decidePolicyForResponseShared(Ref<WebProcessProxy>&& process, PageIdentifier webPageID, FrameIdentifier frameID, FrameInfoData&& frameInfo, PolicyCheckIdentifier identifier, uint64_t navigationID, const ResourceResponse& response, const ResourceRequest& request, bool canShowMIMEType, const String& downloadAttribute, bool wasAllowedByInjectedBundle, uint64_t listenerID, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| m_decidePolicyForResponseRequest = request; |
| |
| WebFrameProxy* frame = process->webFrame(frameID); |
| MESSAGE_CHECK(process, frame); |
| MESSAGE_CHECK_URL(process, request.url()); |
| MESSAGE_CHECK_URL(process, response.url()); |
| RefPtr<API::Navigation> navigation = navigationID ? m_navigationState->navigation(navigationID) : nullptr; |
| auto navigationResponse = API::NavigationResponse::create(API::FrameInfo::create(WTFMove(frameInfo), this).get(), request, response, canShowMIMEType, downloadAttribute); |
| |
| Ref listener = frame->setUpPolicyListenerProxy([this, protectedThis = Ref { *this }, webPageID, frameID, identifier, listenerID, navigation = WTFMove(navigation), |
| process, navigationResponse] (PolicyAction policyAction, API::WebsitePolicies*, ProcessSwapRequestedByClient processSwapRequestedByClient, RefPtr<SafeBrowsingWarning>&& safeBrowsingWarning, std::optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain) mutable { |
| // FIXME: Assert the API::WebsitePolicies* is nullptr here once clients of WKFramePolicyListenerUseWithPolicies go away. |
| RELEASE_ASSERT(processSwapRequestedByClient == ProcessSwapRequestedByClient::No); |
| ASSERT_UNUSED(safeBrowsingWarning, !safeBrowsingWarning); |
| |
| auto sender = PolicyDecisionSender::create(identifier, [webPageID, frameID, listenerID, process] (const auto& policyDecision) { |
| process->send(Messages::WebPage::DidReceivePolicyDecision(frameID, listenerID, policyDecision |
| #if !ENABLE(CONTENT_FILTERING_IN_NETWORKING_PROCESS) |
| , createNetworkExtensionsSandboxExtensions(process) |
| #endif |
| ), webPageID); |
| }); |
| #if USE(QUICK_LOOK) |
| if (policyAction == PolicyAction::Use && process->captivePortalMode() == WebProcessProxy::CaptivePortalMode::Enabled && (MIMETypeRegistry::isPDFOrPostScriptMIMEType(navigationResponse->response().mimeType()) || MIMETypeRegistry::isSupportedModelMIMEType(navigationResponse->response().mimeType()) || PreviewConverter::supportsMIMEType(navigationResponse->response().mimeType()))) |
| policyAction = PolicyAction::Download; |
| #endif |
| receivedPolicyDecision(policyAction, navigation.get(), nullptr, WTFMove(navigationResponse), WTFMove(sender)); |
| }, ShouldExpectSafeBrowsingResult::No, ShouldExpectAppBoundDomainResult::No); |
| |
| if (wasAllowedByInjectedBundle) { |
| listener->use(); |
| return; |
| } |
| |
| if (m_policyClient) |
| m_policyClient->decidePolicyForResponse(*this, *frame, response, request, canShowMIMEType, WTFMove(listener), process->transformHandlesToObjects(userData.object()).get()); |
| else |
| m_navigationClient->decidePolicyForNavigationResponse(*this, WTFMove(navigationResponse), WTFMove(listener), process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| void WebPageProxy::triggerBrowsingContextGroupSwitchForNavigation(uint64_t navigationID, BrowsingContextGroupSwitchDecision browsingContextGroupSwitchDecision, const RegistrableDomain& responseDomain, NetworkResourceLoadIdentifier existingNetworkResourceLoadIdentifierToResume, CompletionHandler<void(bool success)>&& completionHandler) |
| { |
| ASSERT(browsingContextGroupSwitchDecision != BrowsingContextGroupSwitchDecision::StayInGroup); |
| RefPtr<API::Navigation> navigation = navigationID ? m_navigationState->navigation(navigationID) : nullptr; |
| WEBPAGEPROXY_RELEASE_LOG(ProcessSwapping, "triggerBrowsingContextGroupSwitchForNavigation: Process-swapping due to Cross-Origin-Opener-Policy, newProcessIsCrossOriginIsolated=%d, navigation=%p existingNetworkResourceLoadIdentifierToResume=%" PRIu64, browsingContextGroupSwitchDecision == BrowsingContextGroupSwitchDecision::NewIsolatedGroup, navigation.get(), existingNetworkResourceLoadIdentifierToResume.toUInt64()); |
| if (!navigation) |
| return completionHandler(false); |
| |
| RefPtr<WebProcessProxy> processForNavigation; |
| if (browsingContextGroupSwitchDecision == BrowsingContextGroupSwitchDecision::NewIsolatedGroup) |
| processForNavigation = m_process->processPool().createNewWebProcess(&websiteDataStore(), m_process->captivePortalMode(), WebProcessProxy::IsPrewarmed::No, CrossOriginMode::Isolated); |
| else |
| processForNavigation = m_process->processPool().processForRegistrableDomain(websiteDataStore(), responseDomain, m_process->captivePortalMode()); |
| |
| // Tell committed process to stop loading since we're going to do the provisional load in a provisional page now. |
| if (!m_provisionalPage) |
| send(Messages::WebPage::StopLoadingDueToProcessSwap()); |
| continueNavigationInNewProcess(*navigation, nullptr, processForNavigation.releaseNonNull(), ProcessSwapRequestedByClient::No, ShouldTreatAsContinuingLoad::YesAfterProvisionalLoadStarted, existingNetworkResourceLoadIdentifierToResume); |
| completionHandler(true); |
| } |
| |
| void WebPageProxy::unableToImplementPolicy(FrameIdentifier frameID, const ResourceError& error, const UserData& userData) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| if (!m_policyClient) |
| return; |
| m_policyClient->unableToImplementPolicy(*this, *frame, error, m_process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| // FormClient |
| |
| void WebPageProxy::willSubmitForm(FrameIdentifier frameID, FrameIdentifier sourceFrameID, const Vector<std::pair<String, String>>& textFieldValues, FormSubmitListenerIdentifier listenerID, const UserData& userData) |
| { |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| WebFrameProxy* sourceFrame = m_process->webFrame(sourceFrameID); |
| MESSAGE_CHECK(m_process, sourceFrame); |
| |
| for (auto& pair : textFieldValues) |
| MESSAGE_CHECK(m_process, API::Dictionary::MapType::isValidKey(pair.first)); |
| |
| m_formClient->willSubmitForm(*this, *frame, *sourceFrame, textFieldValues, m_process->transformHandlesToObjects(userData.object()).get(), [this, protectedThis = Ref { *this }, frameID, listenerID]() { |
| send(Messages::WebPage::ContinueWillSubmitForm(frameID, listenerID)); |
| }); |
| } |
| |
| #if ENABLE(CONTENT_EXTENSIONS) |
| void WebPageProxy::contentRuleListNotification(URL&& url, ContentRuleListResults&& results) |
| { |
| m_navigationClient->contentRuleListNotification(*this, WTFMove(url), WTFMove(results)); |
| } |
| #endif |
| |
| void WebPageProxy::didNavigateWithNavigationData(const WebNavigationDataStore& store, FrameIdentifier frameID) |
| { |
| didNavigateWithNavigationDataShared(m_process.copyRef(), store, frameID); |
| } |
| |
| void WebPageProxy::didNavigateWithNavigationDataShared(Ref<WebProcessProxy>&& process, const WebNavigationDataStore& store, FrameIdentifier frameID) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didNavigateWithNavigationDataShared:"); |
| |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = process->webFrame(frameID); |
| MESSAGE_CHECK(process, frame); |
| MESSAGE_CHECK(process, frame->page() == this); |
| |
| if (frame->isMainFrame()) |
| m_historyClient->didNavigateWithNavigationData(*this, store); |
| process->processPool().historyClient().didNavigateWithNavigationData(process->processPool(), *this, store, *frame); |
| } |
| |
| void WebPageProxy::didPerformClientRedirect(const String& sourceURLString, const String& destinationURLString, FrameIdentifier frameID) |
| { |
| didPerformClientRedirectShared(m_process.copyRef(), sourceURLString, destinationURLString, frameID); |
| } |
| |
| void WebPageProxy::didPerformClientRedirectShared(Ref<WebProcessProxy>&& process, const String& sourceURLString, const String& destinationURLString, FrameIdentifier frameID) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| if (sourceURLString.isEmpty() || destinationURLString.isEmpty()) |
| return; |
| |
| WebFrameProxy* frame = process->webFrame(frameID); |
| MESSAGE_CHECK(process, frame); |
| MESSAGE_CHECK(process, frame->page() == this); |
| MESSAGE_CHECK_URL(process, sourceURLString); |
| MESSAGE_CHECK_URL(process, destinationURLString); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didPerformClientRedirectShared: frameID=%" PRIu64 ", isMainFrame=%d", frameID.toUInt64(), frame->isMainFrame()); |
| |
| if (frame->isMainFrame()) { |
| m_historyClient->didPerformClientRedirect(*this, sourceURLString, destinationURLString); |
| m_navigationClient->didPerformClientRedirect(*this, sourceURLString, destinationURLString); |
| } |
| process->processPool().historyClient().didPerformClientRedirect(process->processPool(), *this, sourceURLString, destinationURLString, *frame); |
| } |
| |
| void WebPageProxy::didPerformServerRedirect(const String& sourceURLString, const String& destinationURLString, FrameIdentifier frameID) |
| { |
| didPerformServerRedirectShared(m_process.copyRef(), sourceURLString, destinationURLString, frameID); |
| } |
| |
| void WebPageProxy::didPerformServerRedirectShared(Ref<WebProcessProxy>&& process, const String& sourceURLString, const String& destinationURLString, FrameIdentifier frameID) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "didPerformServerRedirect:"); |
| |
| PageClientProtector protector(pageClient()); |
| |
| if (sourceURLString.isEmpty() || destinationURLString.isEmpty()) |
| return; |
| |
| WebFrameProxy* frame = process->webFrame(frameID); |
| MESSAGE_CHECK(process, frame); |
| MESSAGE_CHECK(process, frame->page() == this); |
| |
| MESSAGE_CHECK_URL(process, sourceURLString); |
| MESSAGE_CHECK_URL(process, destinationURLString); |
| |
| if (frame->isMainFrame()) |
| m_historyClient->didPerformServerRedirect(*this, sourceURLString, destinationURLString); |
| process->processPool().historyClient().didPerformServerRedirect(process->processPool(), *this, sourceURLString, destinationURLString, *frame); |
| } |
| |
| void WebPageProxy::didUpdateHistoryTitle(const String& title, const String& url, FrameIdentifier frameID) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| MESSAGE_CHECK(m_process, frame->page() == this); |
| |
| MESSAGE_CHECK_URL(m_process, url); |
| |
| if (frame->isMainFrame()) |
| m_historyClient->didUpdateHistoryTitle(*this, title, url); |
| process().processPool().historyClient().didUpdateHistoryTitle(process().processPool(), *this, title, url, *frame); |
| } |
| |
| // UIClient |
| |
| using NewPageCallback = CompletionHandler<void(RefPtr<WebPageProxy>&&)>; |
| using UIClientCallback = Function<void(Ref<API::NavigationAction>&&, NewPageCallback&&)>; |
| static void trySOAuthorization(Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, NewPageCallback&& newPageCallback, UIClientCallback&& uiClientCallback) |
| { |
| #if HAVE(APP_SSO) |
| if (page.preferences().isExtensibleSSOEnabled()) { |
| page.websiteDataStore().soAuthorizationCoordinator(page).tryAuthorize(WTFMove(navigationAction), page, WTFMove(newPageCallback), WTFMove(uiClientCallback)); |
| return; |
| } |
| #endif |
| uiClientCallback(WTFMove(navigationAction), WTFMove(newPageCallback)); |
| } |
| |
| void WebPageProxy::createNewPage(FrameInfoData&& originatingFrameInfoData, WebPageProxyIdentifier originatingPageID, ResourceRequest&& request, WindowFeatures&& windowFeatures, NavigationActionData&& navigationActionData, Messages::WebPageProxy::CreateNewPage::DelayedReply&& reply) |
| { |
| MESSAGE_CHECK(m_process, originatingFrameInfoData.frameID); |
| MESSAGE_CHECK(m_process, m_process->webFrame(*originatingFrameInfoData.frameID)); |
| |
| auto* originatingPage = m_process->webPage(originatingPageID); |
| auto originatingFrameInfo = API::FrameInfo::create(WTFMove(originatingFrameInfoData), originatingPage); |
| auto mainFrameURL = m_mainFrame ? m_mainFrame->url() : URL(); |
| |
| std::optional<bool> openerAppInitiatedState; |
| if (originatingPage) |
| openerAppInitiatedState = originatingPage->lastNavigationWasAppInitiated(); |
| |
| auto completionHandler = [this, protectedThis = Ref { *this }, mainFrameURL, request, reply = WTFMove(reply), privateClickMeasurement = navigationActionData.privateClickMeasurement, openerAppInitiatedState = WTFMove(openerAppInitiatedState)] (RefPtr<WebPageProxy> newPage) mutable { |
| if (!newPage) { |
| reply(std::nullopt, std::nullopt); |
| return; |
| } |
| |
| newPage->setOpenedByDOM(); |
| |
| if (openerAppInitiatedState) |
| newPage->m_lastNavigationWasAppInitiated = *openerAppInitiatedState; |
| |
| reply(newPage->webPageID(), newPage->creationParameters(m_process, *newPage->drawingArea())); |
| |
| newPage->m_shouldSuppressAppLinksInNextNavigationPolicyDecision = mainFrameURL.host() == request.url().host(); |
| |
| if (privateClickMeasurement) |
| newPage->m_privateClickMeasurement = {{ WTFMove(*privateClickMeasurement), { }, { }}}; |
| #if HAVE(APP_SSO) |
| newPage->m_shouldSuppressSOAuthorizationInNextNavigationPolicyDecision = true; |
| #endif |
| }; |
| |
| RefPtr<API::UserInitiatedAction> userInitiatedActivity; |
| |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| // WebKit cancels the original gesture to open the BBC radio player so |
| // we can call the Storage Access API first. When we re-initiate the open, |
| // we should make sure the client knows that this was user initiated so it |
| // does not block the popup. |
| if (request.url().string() == Quirks::staticRadioPlayerURLString()) |
| userInitiatedActivity = API::UserInitiatedAction::create(); |
| else |
| #endif |
| userInitiatedActivity = m_process->userInitiatedActivity(navigationActionData.userGestureTokenIdentifier); |
| |
| bool shouldOpenAppLinks = originatingFrameInfo->request().url().host() != request.url().host(); |
| auto navigationAction = API::NavigationAction::create(WTFMove(navigationActionData), originatingFrameInfo.ptr(), nullptr, std::nullopt, WTFMove(request), URL(), shouldOpenAppLinks, WTFMove(userInitiatedActivity)); |
| |
| trySOAuthorization(WTFMove(navigationAction), *this, WTFMove(completionHandler), [this, protectedThis = Ref { *this }, windowFeatures = WTFMove(windowFeatures)] (Ref<API::NavigationAction>&& navigationAction, CompletionHandler<void(RefPtr<WebPageProxy>&&)>&& completionHandler) mutable { |
| m_uiClient->createNewPage(*this, WTFMove(windowFeatures), WTFMove(navigationAction), WTFMove(completionHandler)); |
| }); |
| } |
| |
| void WebPageProxy::showPage() |
| { |
| m_uiClient->showPage(this); |
| } |
| |
| void WebPageProxy::exitFullscreenImmediately() |
| { |
| #if ENABLE(FULLSCREEN_API) |
| if (fullScreenManager()) |
| fullScreenManager()->close(); |
| #endif |
| |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| if (videoFullscreenManager()) |
| videoFullscreenManager()->requestHideAndExitFullscreen(); |
| #endif |
| } |
| |
| void WebPageProxy::fullscreenMayReturnToInline() |
| { |
| m_uiClient->fullscreenMayReturnToInline(this); |
| } |
| |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| |
| void WebPageProxy::didEnterFullscreen(PlaybackSessionContextIdentifier identifier) |
| { |
| m_uiClient->didEnterFullscreen(this); |
| |
| m_currentFullscreenVideoSessionIdentifier = identifier; |
| updateFullscreenVideoTextRecognition(); |
| } |
| |
| void WebPageProxy::didExitFullscreen(PlaybackSessionContextIdentifier identifier) |
| { |
| m_uiClient->didExitFullscreen(this); |
| |
| if (m_currentFullscreenVideoSessionIdentifier == identifier) { |
| m_currentFullscreenVideoSessionIdentifier = std::nullopt; |
| updateFullscreenVideoTextRecognition(); |
| } |
| } |
| |
| #else |
| |
| void WebPageProxy::didEnterFullscreen() |
| { |
| m_uiClient->didEnterFullscreen(this); |
| } |
| |
| void WebPageProxy::didExitFullscreen() |
| { |
| m_uiClient->didExitFullscreen(this); |
| } |
| |
| #endif |
| |
| void WebPageProxy::closePage() |
| { |
| if (isClosed()) |
| return; |
| |
| WEBPAGEPROXY_RELEASE_LOG(Process, "closePage:"); |
| pageClient().clearAllEditCommands(); |
| m_uiClient->close(this); |
| } |
| |
| void WebPageProxy::runModalJavaScriptDialog(RefPtr<WebFrameProxy>&& frame, FrameInfoData&& frameInfo, const String& message, CompletionHandler<void(WebPageProxy&, WebFrameProxy* frame, FrameInfoData&& frameInfo, const String& message, CompletionHandler<void()>&&)>&& runDialogCallback) |
| { |
| pageClient().runModalJavaScriptDialog([weakThis = WeakPtr { *this }, frameInfo = WTFMove(frameInfo), frame = WTFMove(frame), message, runDialogCallback = WTFMove(runDialogCallback)]() mutable { |
| RefPtr protectedThis { weakThis.get() }; |
| if (!protectedThis) |
| return; |
| |
| protectedThis->m_isRunningModalJavaScriptDialog = true; |
| runDialogCallback(*protectedThis, frame.get(), WTFMove(frameInfo), message, [weakThis = WTFMove(weakThis)]() mutable { |
| if (RefPtr protectedThis = weakThis.get()) |
| protectedThis->m_isRunningModalJavaScriptDialog = false; |
| }); |
| }); |
| } |
| |
| void WebPageProxy::runJavaScriptAlert(FrameIdentifier frameID, FrameInfoData&& frameInfo, const String& message, Messages::WebPageProxy::RunJavaScriptAlert::DelayedReply&& reply) |
| { |
| RefPtr frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| exitFullscreenImmediately(); |
| |
| // Since runJavaScriptAlert() can spin a nested run loop we need to turn off the responsiveness timer. |
| m_process->stopResponsivenessTimer(); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->willShowJavaScriptDialog(*this); |
| } |
| |
| runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), message, [reply = WTFMove(reply)](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, const String& message, CompletionHandler<void()>&& completion) mutable { |
| page.m_uiClient->runJavaScriptAlert(page, message, frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)]() mutable { |
| reply(); |
| completion(); |
| }); |
| }); |
| } |
| |
| void WebPageProxy::runJavaScriptConfirm(FrameIdentifier frameID, FrameInfoData&& frameInfo, const String& message, Messages::WebPageProxy::RunJavaScriptConfirm::DelayedReply&& reply) |
| { |
| RefPtr frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| exitFullscreenImmediately(); |
| |
| // Since runJavaScriptConfirm() can spin a nested run loop we need to turn off the responsiveness timer. |
| m_process->stopResponsivenessTimer(); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->willShowJavaScriptDialog(*this); |
| } |
| |
| runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), message, [reply = WTFMove(reply)](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, const String& message, CompletionHandler<void()>&& completion) mutable { |
| page.m_uiClient->runJavaScriptConfirm(page, message, frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)](bool result) mutable { |
| reply(result); |
| completion(); |
| }); |
| }); |
| } |
| |
| void WebPageProxy::runJavaScriptPrompt(FrameIdentifier frameID, FrameInfoData&& frameInfo, const String& message, const String& defaultValue, Messages::WebPageProxy::RunJavaScriptPrompt::DelayedReply&& reply) |
| { |
| RefPtr frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| exitFullscreenImmediately(); |
| |
| // Since runJavaScriptPrompt() can spin a nested run loop we need to turn off the responsiveness timer. |
| m_process->stopResponsivenessTimer(); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->willShowJavaScriptDialog(*this); |
| } |
| |
| runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), message, [reply = WTFMove(reply), defaultValue](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, const String& message, CompletionHandler<void()>&& completion) mutable { |
| page.m_uiClient->runJavaScriptPrompt(page, message, defaultValue, frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)](auto& result) mutable { |
| reply(result); |
| completion(); |
| }); |
| }); |
| } |
| |
| void WebPageProxy::setStatusText(const String& text) |
| { |
| m_uiClient->setStatusText(this, text); |
| } |
| |
| void WebPageProxy::mouseDidMoveOverElement(WebHitTestResultData&& hitTestResultData, uint32_t opaqueModifiers, UserData&& userData) |
| { |
| m_lastMouseMoveHitTestResult = API::HitTestResult::create(hitTestResultData); |
| auto modifiers = OptionSet<WebEvent::Modifier>::fromRaw(opaqueModifiers); |
| m_uiClient->mouseDidMoveOverElement(*this, hitTestResultData, modifiers, m_process->transformHandlesToObjects(userData.object()).get()); |
| setToolTip(hitTestResultData.toolTipText); |
| } |
| |
| #if ENABLE(WEBGL) |
| void WebPageProxy::webGLPolicyForURL(URL&& url, Messages::WebPageProxy::WebGLPolicyForURL::DelayedReply&& reply) |
| { |
| m_navigationClient->webGLLoadPolicy(*this, url, WTFMove(reply)); |
| } |
| |
| void WebPageProxy::resolveWebGLPolicyForURL(URL&& url, Messages::WebPageProxy::ResolveWebGLPolicyForURL::DelayedReply&& reply) |
| { |
| m_navigationClient->resolveWebGLLoadPolicy(*this, url, WTFMove(reply)); |
| } |
| #endif // ENABLE(WEBGL) |
| |
| void WebPageProxy::setToolbarsAreVisible(bool toolbarsAreVisible) |
| { |
| m_uiClient->setToolbarsAreVisible(*this, toolbarsAreVisible); |
| } |
| |
| void WebPageProxy::getToolbarsAreVisible(Messages::WebPageProxy::GetToolbarsAreVisible::DelayedReply&& reply) |
| { |
| m_uiClient->toolbarsAreVisible(*this, WTFMove(reply)); |
| } |
| |
| void WebPageProxy::setMenuBarIsVisible(bool menuBarIsVisible) |
| { |
| m_uiClient->setMenuBarIsVisible(*this, menuBarIsVisible); |
| } |
| |
| void WebPageProxy::getMenuBarIsVisible(Messages::WebPageProxy::GetMenuBarIsVisible::DelayedReply&& reply) |
| { |
| m_uiClient->menuBarIsVisible(*this, WTFMove(reply)); |
| } |
| |
| void WebPageProxy::setStatusBarIsVisible(bool statusBarIsVisible) |
| { |
| m_uiClient->setStatusBarIsVisible(*this, statusBarIsVisible); |
| } |
| |
| void WebPageProxy::getStatusBarIsVisible(Messages::WebPageProxy::GetStatusBarIsVisible::DelayedReply&& reply) |
| { |
| m_uiClient->statusBarIsVisible(*this, WTFMove(reply)); |
| } |
| |
| void WebPageProxy::setIsResizable(bool isResizable) |
| { |
| m_uiClient->setIsResizable(*this, isResizable); |
| } |
| |
| void WebPageProxy::setWindowFrame(const FloatRect& newWindowFrame) |
| { |
| m_uiClient->setWindowFrame(*this, pageClient().convertToDeviceSpace(newWindowFrame)); |
| } |
| |
| void WebPageProxy::getWindowFrame(Messages::WebPageProxy::GetWindowFrame::DelayedReply&& reply) |
| { |
| m_uiClient->windowFrame(*this, [this, protectedThis = Ref { *this }, reply = WTFMove(reply)] (FloatRect frame) mutable { |
| reply(pageClient().convertToUserSpace(frame)); |
| }); |
| } |
| |
| void WebPageProxy::getWindowFrameWithCallback(Function<void(FloatRect)>&& completionHandler) |
| { |
| m_uiClient->windowFrame(*this, [this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)] (FloatRect frame) { |
| completionHandler(pageClient().convertToUserSpace(frame)); |
| }); |
| } |
| |
| void WebPageProxy::screenToRootView(const IntPoint& screenPoint, Messages::WebPageProxy::ScreenToRootView::DelayedReply&& reply) |
| { |
| reply(pageClient().screenToRootView(screenPoint)); |
| } |
| |
| void WebPageProxy::rootViewToScreen(const IntRect& viewRect, Messages::WebPageProxy::RootViewToScreen::DelayedReply&& reply) |
| { |
| reply(pageClient().rootViewToScreen(viewRect)); |
| } |
| |
| IntRect WebPageProxy::syncRootViewToScreen(const IntRect& viewRect) |
| { |
| return pageClient().rootViewToScreen(viewRect); |
| } |
| |
| void WebPageProxy::accessibilityScreenToRootView(const IntPoint& screenPoint, CompletionHandler<void(IntPoint)>&& completionHandler) |
| { |
| completionHandler(pageClient().accessibilityScreenToRootView(screenPoint)); |
| } |
| |
| void WebPageProxy::rootViewToAccessibilityScreen(const IntRect& viewRect, CompletionHandler<void(IntRect)>&& completionHandler) |
| { |
| completionHandler(pageClient().rootViewToAccessibilityScreen(viewRect)); |
| } |
| |
| void WebPageProxy::runBeforeUnloadConfirmPanel(FrameIdentifier frameID, FrameInfoData&& frameInfo, const String& message, Messages::WebPageProxy::RunBeforeUnloadConfirmPanel::DelayedReply&& reply) |
| { |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| // Per §18 User Prompts in the WebDriver spec, "User prompts that are spawned from beforeunload |
| // event handlers, are dismissed implicitly upon navigation or close window, regardless of the |
| // defined user prompt handler." So, always allow the unload to proceed if the page is being automated. |
| if (m_controlledByAutomation) { |
| if (!!process().processPool().automationSession()) { |
| reply(true); |
| return; |
| } |
| } |
| |
| // Since runBeforeUnloadConfirmPanel() can spin a nested run loop we need to turn off the responsiveness timer and the tryClose timer. |
| m_process->stopResponsivenessTimer(); |
| bool shouldResumeTimerAfterPrompt = m_tryCloseTimeoutTimer.isActive(); |
| m_tryCloseTimeoutTimer.stop(); |
| m_uiClient->runBeforeUnloadConfirmPanel(*this, message, frame, WTFMove(frameInfo), |
| [this, weakThis = WeakPtr { *this }, completionHandler = WTFMove(reply), shouldResumeTimerAfterPrompt](bool shouldClose) mutable { |
| if (weakThis && shouldResumeTimerAfterPrompt) |
| m_tryCloseTimeoutTimer.startOneShot(tryCloseTimeoutDelay); |
| completionHandler(shouldClose); |
| }); |
| } |
| |
| void WebPageProxy::didChangeViewportProperties(const ViewportAttributes& attr) |
| { |
| pageClient().didChangeViewportProperties(attr); |
| } |
| |
| void WebPageProxy::pageDidScroll(const WebCore::IntPoint& scrollPosition) |
| { |
| m_uiClient->pageDidScroll(this); |
| |
| pageClient().pageDidScroll(scrollPosition); |
| |
| #if PLATFORM(IOS_FAMILY) |
| // Do not hide the validation message if the scrolling was caused by the keyboard showing up. |
| if (m_isKeyboardAnimatingIn) |
| return; |
| #endif |
| |
| #if !PLATFORM(IOS_FAMILY) |
| closeOverlayedViews(); |
| #endif |
| } |
| |
| void WebPageProxy::setHasActiveAnimatedScrolls(bool isRunning) |
| { |
| m_hasActiveAnimatedScroll = isRunning; |
| #if HAVE(CVDISPLAYLINK) |
| updateDisplayLinkFrequency(); |
| #endif |
| } |
| |
| void WebPageProxy::runOpenPanel(FrameIdentifier frameID, FrameInfoData&& frameInfo, const FileChooserSettings& settings) |
| { |
| if (m_openPanelResultListener) { |
| m_openPanelResultListener->invalidate(); |
| m_openPanelResultListener = nullptr; |
| } |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| Ref<API::OpenPanelParameters> parameters = API::OpenPanelParameters::create(settings); |
| m_openPanelResultListener = WebOpenPanelResultListenerProxy::create(this); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->handleRunOpenPanel(*this, *frame, parameters.get(), *m_openPanelResultListener); |
| |
| // Don't show a file chooser, since automation will be unable to interact with it. |
| return; |
| } |
| |
| // Since runOpenPanel() can spin a nested run loop we need to turn off the responsiveness timer. |
| m_process->stopResponsivenessTimer(); |
| |
| const auto frameInfoForPageClient = frameInfo; |
| |
| if (!m_uiClient->runOpenPanel(*this, frame, WTFMove(frameInfo), parameters.ptr(), m_openPanelResultListener.get())) { |
| if (!pageClient().handleRunOpenPanel(this, frame, frameInfoForPageClient, parameters.ptr(), m_openPanelResultListener.get())) |
| didCancelForOpenPanel(); |
| } |
| } |
| |
| void WebPageProxy::showShareSheet(const ShareDataWithParsedURL& shareData, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| MESSAGE_CHECK(m_process, !shareData.url || shareData.url->protocolIsInHTTPFamily() || shareData.url->protocolIsData()); |
| MESSAGE_CHECK(m_process, shareData.files.isEmpty() || m_preferences->webShareFileAPIEnabled()); |
| pageClient().showShareSheet(shareData, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::showContactPicker(const WebCore::ContactsRequestData& requestData, CompletionHandler<void(std::optional<Vector<WebCore::ContactInfo>>&&)>&& completionHandler) |
| { |
| MESSAGE_CHECK(m_process, m_preferences->contactPickerAPIEnabled()); |
| pageClient().showContactPicker(requestData, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::printFrame(FrameIdentifier frameID, const String& title, const WebCore::FloatSize& pdfFirstPageSize, CompletionHandler<void()>&& completionHandler) |
| { |
| ASSERT(!m_isPerformingDOMPrintOperation); |
| m_isPerformingDOMPrintOperation = true; |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| frame->didChangeTitle(title); |
| |
| m_uiClient->printFrame(*this, *frame, pdfFirstPageSize, [this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler)] () mutable { |
| endPrinting(); // Send a message synchronously while m_isPerformingDOMPrintOperation is still true. |
| m_isPerformingDOMPrintOperation = false; |
| completionHandler(); |
| }); |
| } |
| |
| void WebPageProxy::setMediaVolume(float volume) |
| { |
| if (volume == m_mediaVolume) |
| return; |
| |
| m_mediaVolume = volume; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetMediaVolume(volume)); |
| } |
| |
| void WebPageProxy::setMuted(WebCore::MediaProducerMutedStateFlags state, CompletionHandler<void()>&& completionHandler) |
| { |
| if (!isAllowedToChangeMuteState()) |
| state.add(WebCore::MediaProducer::MediaStreamCaptureIsMuted); |
| |
| m_mutedState = state; |
| |
| if (!hasRunningProcess()) |
| return completionHandler(); |
| |
| #if ENABLE(MEDIA_STREAM) |
| bool hasMutedCaptureStreams = m_mediaState.containsAny(WebCore::MediaProducer::MutedCaptureMask); |
| if (hasMutedCaptureStreams && !(state.containsAny(WebCore::MediaProducer::MediaStreamCaptureIsMuted))) |
| WebProcessProxy::muteCaptureInPagesExcept(m_webPageID); |
| #endif |
| |
| m_process->pageMutedStateChanged(m_webPageID, state); |
| |
| sendWithAsyncReply(Messages::WebPage::SetMuted(state), WTFMove(completionHandler)); |
| activityStateDidChange({ ActivityState::IsAudible, ActivityState::IsCapturingMedia }); |
| } |
| |
| void WebPageProxy::setMediaCaptureEnabled(bool enabled) |
| { |
| m_mediaCaptureEnabled = enabled; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| #if ENABLE(MEDIA_STREAM) |
| UserMediaProcessManager::singleton().setCaptureEnabled(enabled); |
| #endif |
| } |
| |
| void WebPageProxy::stopMediaCapture(MediaProducerMediaCaptureKind kind, CompletionHandler<void()>&& completionHandler) |
| { |
| if (!hasRunningProcess()) |
| return completionHandler(); |
| |
| #if ENABLE(MEDIA_STREAM) |
| if (m_userMediaPermissionRequestManager) |
| m_userMediaPermissionRequestManager->resetAccess(); |
| sendWithAsyncReply(Messages::WebPage::StopMediaCapture(kind), WTFMove(completionHandler)); |
| #endif |
| } |
| |
| void WebPageProxy::requestMediaPlaybackState(CompletionHandler<void(MediaPlaybackState)>&& completionHandler) |
| { |
| if (!hasRunningProcess()) { |
| completionHandler({ }); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::RequestMediaPlaybackState(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::pauseAllMediaPlayback(CompletionHandler<void()>&& completionHandler) |
| { |
| if (!hasRunningProcess()) { |
| completionHandler(); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::PauseAllMediaPlayback(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::suspendAllMediaPlayback(CompletionHandler<void()>&& completionHandler) |
| { |
| m_suspendMediaPlaybackCounter++; |
| if (m_mediaPlaybackIsSuspended) { |
| completionHandler(); |
| return; |
| } |
| m_mediaPlaybackIsSuspended = true; |
| |
| if (!hasRunningProcess()) { |
| completionHandler(); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::SuspendAllMediaPlayback(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::resumeAllMediaPlayback(CompletionHandler<void()>&& completionHandler) |
| { |
| if (m_suspendMediaPlaybackCounter > 0) |
| m_suspendMediaPlaybackCounter--; |
| |
| if (!m_mediaPlaybackIsSuspended || m_suspendMediaPlaybackCounter) { |
| completionHandler(); |
| return; |
| } |
| m_mediaPlaybackIsSuspended = false; |
| |
| if (!hasRunningProcess()) { |
| completionHandler(); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::ResumeAllMediaPlayback(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setMayStartMediaWhenInWindow(bool mayStartMedia) |
| { |
| if (mayStartMedia == m_mayStartMediaWhenInWindow) |
| return; |
| |
| m_mayStartMediaWhenInWindow = mayStartMedia; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetMayStartMediaWhenInWindow(mayStartMedia)); |
| } |
| |
| void WebPageProxy::handleDownloadRequest(DownloadProxy& download) |
| { |
| pageClient().handleDownloadRequest(download); |
| } |
| |
| void WebPageProxy::resumeDownload(const API::Data& resumeData, const String& path, CompletionHandler<void(DownloadProxy*)>&& completionHandler) |
| { |
| auto& download = process().processPool().resumeDownload(websiteDataStore(), this, resumeData, path, CallDownloadDidStart::Yes); |
| download.setDestinationFilename(path); |
| download.setDidStartCallback(WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::downloadRequest(WebCore::ResourceRequest&& request, CompletionHandler<void(DownloadProxy*)>&& completionHandler) |
| { |
| auto& download = process().processPool().download(websiteDataStore(), this, request, { }); |
| download.setDidStartCallback(WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::dataTaskWithRequest(WebCore::ResourceRequest&& request, CompletionHandler<void(API::DataTask&)>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().dataTaskWithRequest(*this, sessionID(), WTFMove(request), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::didChangeContentSize(const IntSize& size) |
| { |
| pageClient().didChangeContentSize(size); |
| } |
| |
| void WebPageProxy::didChangeIntrinsicContentSize(const IntSize& intrinsicContentSize) |
| { |
| #if USE(APPKIT) |
| pageClient().intrinsicContentSizeDidChange(intrinsicContentSize); |
| #endif |
| } |
| |
| #if ENABLE(INPUT_TYPE_COLOR) |
| void WebPageProxy::showColorPicker(const WebCore::Color& initialColor, const IntRect& elementRect, Vector<WebCore::Color>&& suggestions) |
| { |
| m_colorPicker = pageClient().createColorPicker(this, initialColor, elementRect, WTFMove(suggestions)); |
| m_colorPicker->showColorPicker(initialColor); |
| } |
| |
| void WebPageProxy::setColorPickerColor(const WebCore::Color& color) |
| { |
| if (m_colorPicker) |
| m_colorPicker->setSelectedColor(color); |
| } |
| |
| void WebPageProxy::endColorPicker() |
| { |
| if (auto colorPicker = std::exchange(m_colorPicker, nullptr)) |
| colorPicker->endPicker(); |
| } |
| |
| void WebPageProxy::didChooseColor(const WebCore::Color& color) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::DidChooseColor(color)); |
| } |
| |
| void WebPageProxy::didEndColorPicker() |
| { |
| if (std::exchange(m_colorPicker, nullptr)) { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::DidEndColorPicker()); |
| } |
| |
| } |
| #endif |
| |
| #if ENABLE(DATALIST_ELEMENT) |
| |
| void WebPageProxy::showDataListSuggestions(WebCore::DataListSuggestionInformation&& info) |
| { |
| if (!m_dataListSuggestionsDropdown) |
| m_dataListSuggestionsDropdown = pageClient().createDataListSuggestionsDropdown(*this); |
| |
| m_dataListSuggestionsDropdown->show(WTFMove(info)); |
| } |
| |
| void WebPageProxy::handleKeydownInDataList(const String& key) |
| { |
| if (!m_dataListSuggestionsDropdown) |
| return; |
| |
| m_dataListSuggestionsDropdown->handleKeydownWithIdentifier(key); |
| } |
| |
| void WebPageProxy::endDataListSuggestions() |
| { |
| if (m_dataListSuggestionsDropdown) |
| m_dataListSuggestionsDropdown->close(); |
| } |
| |
| void WebPageProxy::didCloseSuggestions() |
| { |
| if (!m_dataListSuggestionsDropdown) |
| return; |
| |
| m_dataListSuggestionsDropdown = nullptr; |
| send(Messages::WebPage::DidCloseSuggestions()); |
| } |
| |
| void WebPageProxy::didSelectOption(const String& selectedOption) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::DidSelectDataListOption(selectedOption)); |
| } |
| |
| #endif |
| |
| #if ENABLE(DATE_AND_TIME_INPUT_TYPES) |
| |
| void WebPageProxy::showDateTimePicker(WebCore::DateTimeChooserParameters&& params) |
| { |
| if (!m_dateTimePicker) |
| m_dateTimePicker = pageClient().createDateTimePicker(*this); |
| |
| m_dateTimePicker->showDateTimePicker(WTFMove(params)); |
| } |
| |
| void WebPageProxy::endDateTimePicker() |
| { |
| if (!m_dateTimePicker) |
| return; |
| |
| m_dateTimePicker->endPicker(); |
| } |
| |
| void WebPageProxy::didChooseDate(StringView date) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::DidChooseDate(date.toString())); |
| } |
| |
| void WebPageProxy::didEndDateTimePicker() |
| { |
| m_dateTimePicker = nullptr; |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::DidEndDateTimePicker()); |
| } |
| |
| #endif |
| |
| WebInspectorUIProxy* WebPageProxy::inspector() const |
| { |
| if (isClosed()) |
| return nullptr; |
| return m_inspector.get(); |
| } |
| |
| void WebPageProxy::resourceLoadDidSendRequest(ResourceLoadInfo&& loadInfo, WebCore::ResourceRequest&& request) |
| { |
| if (m_resourceLoadClient) |
| m_resourceLoadClient->didSendRequest(WTFMove(loadInfo), WTFMove(request)); |
| } |
| |
| void WebPageProxy::resourceLoadDidPerformHTTPRedirection(ResourceLoadInfo&& loadInfo, WebCore::ResourceResponse&& response, WebCore::ResourceRequest&& request) |
| { |
| if (m_resourceLoadClient) |
| m_resourceLoadClient->didPerformHTTPRedirection(WTFMove(loadInfo), WTFMove(response), WTFMove(request)); |
| } |
| |
| void WebPageProxy::resourceLoadDidReceiveChallenge(ResourceLoadInfo&& loadInfo, WebCore::AuthenticationChallenge&& challenge) |
| { |
| if (m_resourceLoadClient) |
| m_resourceLoadClient->didReceiveChallenge(WTFMove(loadInfo), WTFMove(challenge)); |
| } |
| |
| void WebPageProxy::resourceLoadDidReceiveResponse(ResourceLoadInfo&& loadInfo, WebCore::ResourceResponse&& response) |
| { |
| if (m_resourceLoadClient) |
| m_resourceLoadClient->didReceiveResponse(WTFMove(loadInfo), WTFMove(response)); |
| } |
| |
| void WebPageProxy::resourceLoadDidCompleteWithError(ResourceLoadInfo&& loadInfo, WebCore::ResourceResponse&& response, WebCore::ResourceError&& error) |
| { |
| if (m_resourceLoadClient) |
| m_resourceLoadClient->didCompleteWithError(WTFMove(loadInfo), WTFMove(response), WTFMove(error)); |
| } |
| |
| #if ENABLE(FULLSCREEN_API) |
| WebFullScreenManagerProxy* WebPageProxy::fullScreenManager() |
| { |
| return m_fullScreenManager.get(); |
| } |
| |
| void WebPageProxy::setFullscreenClient(std::unique_ptr<API::FullscreenClient>&& client) |
| { |
| if (!client) { |
| m_fullscreenClient = makeUnique<API::FullscreenClient>(); |
| return; |
| } |
| |
| m_fullscreenClient = WTFMove(client); |
| } |
| #endif |
| |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| PlaybackSessionManagerProxy* WebPageProxy::playbackSessionManager() |
| { |
| return m_playbackSessionManager.get(); |
| } |
| |
| VideoFullscreenManagerProxy* WebPageProxy::videoFullscreenManager() |
| { |
| return m_videoFullscreenManager.get(); |
| } |
| |
| void WebPageProxy::setMockVideoPresentationModeEnabled(bool enabled) |
| { |
| m_mockVideoPresentationModeEnabled = enabled; |
| if (m_videoFullscreenManager) |
| m_videoFullscreenManager->setMockVideoPresentationModeEnabled(enabled); |
| } |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) |
| bool WebPageProxy::allowsMediaDocumentInlinePlayback() const |
| { |
| return m_allowsMediaDocumentInlinePlayback; |
| } |
| |
| void WebPageProxy::setAllowsMediaDocumentInlinePlayback(bool allows) |
| { |
| if (m_allowsMediaDocumentInlinePlayback == allows) |
| return; |
| m_allowsMediaDocumentInlinePlayback = allows; |
| |
| send(Messages::WebPage::SetAllowsMediaDocumentInlinePlayback(allows)); |
| } |
| #endif |
| |
| void WebPageProxy::setHasHadSelectionChangesFromUserInteraction(bool hasHadUserSelectionChanges) |
| { |
| m_hasHadSelectionChangesFromUserInteraction = hasHadUserSelectionChanges; |
| } |
| |
| #if HAVE(TOUCH_BAR) |
| |
| void WebPageProxy::setIsTouchBarUpdateSupressedForHiddenContentEditable(bool ignoreTouchBarUpdate) |
| { |
| m_isTouchBarUpdateSupressedForHiddenContentEditable = ignoreTouchBarUpdate; |
| } |
| |
| void WebPageProxy::setIsNeverRichlyEditableForTouchBar(bool isNeverRichlyEditable) |
| { |
| m_isNeverRichlyEditableForTouchBar = isNeverRichlyEditable; |
| } |
| |
| #endif |
| |
| void WebPageProxy::requestDOMPasteAccess(WebCore::DOMPasteAccessCategory pasteAccessCategory, const WebCore::IntRect& elementRect, const String& originIdentifier, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completionHandler) |
| { |
| MESSAGE_CHECK_COMPLETION(m_process, !originIdentifier.isEmpty(), completionHandler(DOMPasteAccessResponse::DeniedForGesture)); |
| |
| m_pageClient->requestDOMPasteAccess(pasteAccessCategory, elementRect, originIdentifier, WTFMove(completionHandler)); |
| } |
| |
| // BackForwardList |
| |
| void WebPageProxy::backForwardAddItem(BackForwardListItemState&& itemState) |
| { |
| auto item = WebBackForwardListItem::create(WTFMove(itemState), identifier()); |
| item->setResourceDirectoryURL(currentResourceDirectoryURL()); |
| m_backForwardList->addItem(WTFMove(item)); |
| } |
| |
| void WebPageProxy::backForwardGoToItem(const BackForwardItemIdentifier& itemID, CompletionHandler<void(const WebBackForwardListCounts&)>&& completionHandler) |
| { |
| // On process swap, we tell the previous process to ignore the load, which causes it so restore its current back forward item to its previous |
| // value. Since the load is really going on in a new provisional process, we want to ignore such requests from the committed process. |
| // Any real new load in the committed process would have cleared m_provisionalPage. |
| if (m_provisionalPage) |
| return completionHandler(m_backForwardList->counts()); |
| |
| backForwardGoToItemShared(m_process.copyRef(), itemID, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::backForwardListContainsItem(const WebCore::BackForwardItemIdentifier& itemID, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| completionHandler(m_backForwardList->itemForID(itemID)); |
| } |
| |
| void WebPageProxy::backForwardGoToItemShared(Ref<WebProcessProxy>&& process, const BackForwardItemIdentifier& itemID, CompletionHandler<void(const WebBackForwardListCounts&)>&& completionHandler) |
| { |
| MESSAGE_CHECK_COMPLETION(m_process, !WebKit::isInspectorPage(*this), completionHandler(m_backForwardList->counts())); |
| |
| auto* item = m_backForwardList->itemForID(itemID); |
| if (!item) |
| return completionHandler(m_backForwardList->counts()); |
| |
| m_backForwardList->goToItem(*item); |
| completionHandler(m_backForwardList->counts()); |
| } |
| |
| void WebPageProxy::backForwardItemAtIndex(int32_t index, CompletionHandler<void(std::optional<BackForwardItemIdentifier>&&)>&& completionHandler) |
| { |
| if (auto* item = m_backForwardList->itemAtIndex(index)) |
| completionHandler(item->itemID()); |
| else |
| completionHandler(std::nullopt); |
| } |
| |
| void WebPageProxy::backForwardListCounts(Messages::WebPageProxy::BackForwardListCountsDelayedReply&& completionHandler) |
| { |
| completionHandler(m_backForwardList->counts()); |
| } |
| |
| void WebPageProxy::compositionWasCanceled() |
| { |
| #if PLATFORM(COCOA) |
| pageClient().notifyInputContextAboutDiscardedComposition(); |
| #endif |
| } |
| |
| // Undo management |
| |
| void WebPageProxy::registerEditCommandForUndo(WebUndoStepID commandID, const String& label) |
| { |
| registerEditCommand(WebEditCommandProxy::create(commandID, label, *this), UndoOrRedo::Undo); |
| } |
| |
| void WebPageProxy::registerInsertionUndoGrouping() |
| { |
| #if USE(INSERTION_UNDO_GROUPING) |
| pageClient().registerInsertionUndoGrouping(); |
| #endif |
| } |
| |
| void WebPageProxy::canUndoRedo(UndoOrRedo action, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| completionHandler(pageClient().canUndoRedo(action)); |
| } |
| |
| void WebPageProxy::executeUndoRedo(UndoOrRedo action, CompletionHandler<void()>&& completionHandler) |
| { |
| pageClient().executeUndoRedo(action); |
| completionHandler(); |
| } |
| |
| void WebPageProxy::clearAllEditCommands() |
| { |
| pageClient().clearAllEditCommands(); |
| } |
| |
| void WebPageProxy::didCountStringMatches(const String& string, uint32_t matchCount) |
| { |
| m_findClient->didCountStringMatches(this, string, matchCount); |
| } |
| |
| void WebPageProxy::didGetImageForFindMatch(const ImageBufferBackend::Parameters& parameters, ShareableBitmap::Handle contentImageHandle, uint32_t matchIndex) |
| { |
| auto image = WebImage::create(parameters, WTFMove(contentImageHandle)); |
| if (!image) { |
| ASSERT_NOT_REACHED(); |
| return; |
| } |
| m_findMatchesClient->didGetImageForMatchResult(this, image.get(), matchIndex); |
| } |
| |
| void WebPageProxy::setTextIndicator(const TextIndicatorData& indicatorData, uint64_t lifetime) |
| { |
| // FIXME: Make TextIndicatorWindow a platform-independent presentational thing ("TextIndicatorPresentation"?). |
| #if PLATFORM(COCOA) |
| pageClient().setTextIndicator(TextIndicator::create(indicatorData), static_cast<WebCore::TextIndicatorLifetime>(lifetime)); |
| #else |
| ASSERT_NOT_REACHED(); |
| #endif |
| } |
| |
| void WebPageProxy::clearTextIndicator() |
| { |
| #if PLATFORM(COCOA) |
| pageClient().clearTextIndicator(WebCore::TextIndicatorDismissalAnimation::FadeOut); |
| #else |
| ASSERT_NOT_REACHED(); |
| #endif |
| } |
| |
| void WebPageProxy::setTextIndicatorAnimationProgress(float progress) |
| { |
| #if PLATFORM(COCOA) |
| pageClient().setTextIndicatorAnimationProgress(progress); |
| #else |
| ASSERT_NOT_REACHED(); |
| #endif |
| } |
| |
| void WebPageProxy::didFindString(const String& string, const Vector<WebCore::IntRect>& matchRects, uint32_t matchCount, int32_t matchIndex, bool didWrapAround) |
| { |
| m_findClient->didFindString(this, string, matchRects, matchCount, matchIndex, didWrapAround); |
| } |
| |
| void WebPageProxy::didFindStringMatches(const String& string, const Vector<Vector<WebCore::IntRect>>& matchRects, int32_t firstIndexAfterSelection) |
| { |
| m_findMatchesClient->didFindStringMatches(this, string, matchRects, firstIndexAfterSelection); |
| } |
| |
| void WebPageProxy::didFailToFindString(const String& string) |
| { |
| m_findClient->didFailToFindString(this, string); |
| } |
| |
| bool WebPageProxy::sendMessage(UniqueRef<IPC::Encoder>&& encoder, OptionSet<IPC::SendOption> sendOptions, std::optional<std::pair<CompletionHandler<void(IPC::Decoder*)>, uint64_t>>&& asyncReplyInfo) |
| { |
| return m_process->sendMessage(WTFMove(encoder), sendOptions, WTFMove(asyncReplyInfo)); |
| } |
| |
| IPC::Connection* WebPageProxy::messageSenderConnection() const |
| { |
| return m_process->hasConnection() ? m_process->connection() : nullptr; |
| } |
| |
| uint64_t WebPageProxy::messageSenderDestinationID() const |
| { |
| return m_webPageID.toUInt64(); |
| } |
| |
| void WebPageProxy::valueChangedForPopupMenu(WebPopupMenuProxy*, int32_t newSelectedIndex) |
| { |
| send(Messages::WebPage::DidChangeSelectedIndexForActivePopupMenu(newSelectedIndex)); |
| } |
| |
| void WebPageProxy::setTextFromItemForPopupMenu(WebPopupMenuProxy*, int32_t index) |
| { |
| send(Messages::WebPage::SetTextForActivePopupMenu(index)); |
| } |
| |
| bool WebPageProxy::isProcessingKeyboardEvents() const |
| { |
| return !m_keyEventQueue.isEmpty(); |
| } |
| |
| bool WebPageProxy::isProcessingMouseEvents() const |
| { |
| return !m_mouseEventQueue.isEmpty(); |
| } |
| |
| bool WebPageProxy::isProcessingWheelEvents() const |
| { |
| return m_wheelEventCoalescer && m_wheelEventCoalescer->hasEventsBeingProcessed(); |
| } |
| |
| NativeWebMouseEvent* WebPageProxy::currentlyProcessedMouseDownEvent() |
| { |
| // <https://bugs.webkit.org/show_bug.cgi?id=57904> We need to keep track of the mouse down event in the case where we |
| // display a popup menu for select elements. When the user changes the selected item, we fake a mouseup event by |
| // using this stored mousedown event and changing the event type. This trickery happens when WebProcess handles |
| // a mousedown event that runs the default handler for HTMLSelectElement, so the triggering mousedown must be the first event. |
| |
| if (m_mouseEventQueue.isEmpty()) |
| return nullptr; |
| |
| auto& event = m_mouseEventQueue.first(); |
| if (event.type() != WebEvent::Type::MouseDown) |
| return nullptr; |
| |
| return &event; |
| } |
| |
| void WebPageProxy::postMessageToInjectedBundle(const String& messageName, API::Object* messageBody) |
| { |
| if (!hasRunningProcess()) { |
| m_pendingInjectedBundleMessages.append(InjectedBundleMessage { messageName, messageBody }); |
| return; |
| } |
| |
| send(Messages::WebPage::PostInjectedBundleMessage(messageName, UserData(process().transformObjectsToHandles(messageBody).get()))); |
| } |
| |
| #if PLATFORM(GTK) |
| void WebPageProxy::failedToShowPopupMenu() |
| { |
| send(Messages::WebPage::FailedToShowPopupMenu()); |
| } |
| #endif |
| |
| void WebPageProxy::showPopupMenu(const IntRect& rect, uint64_t textDirection, const Vector<WebPopupItem>& items, int32_t selectedIndex, const PlatformPopupMenuData& data) |
| { |
| MESSAGE_CHECK(m_process, selectedIndex == -1 || static_cast<uint32_t>(selectedIndex) < items.size()); |
| |
| if (m_activePopupMenu) { |
| m_activePopupMenu->hidePopupMenu(); |
| m_activePopupMenu->invalidate(); |
| m_activePopupMenu = nullptr; |
| } |
| |
| // If the page is controlled by automation, entering a nested run loop while the menu is open |
| // can hang the page / WebDriver test. Since <option> elements are selected via a different |
| // code path anyway, just don't show the native popup menu. |
| if (auto* automationSession = process().processPool().automationSession()) { |
| if (m_controlledByAutomation && automationSession->isSimulatingUserInteraction()) |
| return; |
| } |
| |
| m_activePopupMenu = pageClient().createPopupMenuProxy(*this); |
| |
| if (!m_activePopupMenu) |
| return; |
| |
| // Since showPopupMenu() can spin a nested run loop we need to turn off the responsiveness timer. |
| m_process->stopResponsivenessTimer(); |
| |
| // Showing a popup menu runs a nested runloop, which can handle messages that cause |this| to get closed. |
| Ref<WebPageProxy> protect(*this); |
| m_activePopupMenu->showPopupMenu(rect, static_cast<TextDirection>(textDirection), m_pageScaleFactor, items, data, selectedIndex); |
| } |
| |
| void WebPageProxy::hidePopupMenu() |
| { |
| if (!m_activePopupMenu) |
| return; |
| |
| m_activePopupMenu->hidePopupMenu(); |
| m_activePopupMenu->invalidate(); |
| m_activePopupMenu = nullptr; |
| } |
| |
| #if ENABLE(CONTEXT_MENUS) |
| void WebPageProxy::showContextMenu(ContextMenuContextData&& contextMenuContextData, const UserData& userData) |
| { |
| // Showing a context menu runs a nested runloop, which can handle messages that cause |this| to get closed. |
| Ref<WebPageProxy> protect(*this); |
| |
| // If the page is controlled by automation, entering a nested run loop while the menu is open |
| // can hang the page / WebDriver test. Pretend to show and immediately dismiss the context menu. |
| if (auto* automationSession = process().processPool().automationSession()) { |
| if (m_controlledByAutomation && automationSession->isSimulatingUserInteraction()) { |
| send(Messages::WebPage::DidShowContextMenu()); |
| return; |
| } |
| } |
| |
| // Discard any enqueued mouse events that have been delivered to the UIProcess whilst the WebProcess is still processing the |
| // MouseDown event that triggered this ShowContextMenu message. This can happen if we take too long to enter the nested runloop. |
| discardQueuedMouseEvents(); |
| |
| m_activeContextMenuContextData = contextMenuContextData; |
| |
| m_activeContextMenu = pageClient().createContextMenuProxy(*this, WTFMove(contextMenuContextData), userData); |
| |
| m_activeContextMenu->show(); |
| } |
| |
| void WebPageProxy::didShowContextMenu() |
| { |
| // Don't send `Messages::WebPage::DidShowContextMenu` as that should've already been eagerly |
| // sent when requesting the context menu to show, regardless of the result of that request. |
| |
| pageClient().didShowContextMenu(); |
| } |
| |
| void WebPageProxy::didDismissContextMenu() |
| { |
| send(Messages::WebPage::DidDismissContextMenu()); |
| |
| pageClient().didDismissContextMenu(); |
| } |
| |
| void WebPageProxy::contextMenuItemSelected(const WebContextMenuItemData& item) |
| { |
| // Application custom items don't need to round-trip through to WebCore in the WebProcess. |
| if (item.action() >= ContextMenuItemBaseApplicationTag) { |
| m_contextMenuClient->customContextMenuItemSelected(*this, item); |
| return; |
| } |
| |
| struct DownloadInfo { |
| String url; |
| String suggestedFilename; |
| }; |
| std::optional<DownloadInfo> downloadInfo; |
| |
| ASSERT(m_activeContextMenuContextData.webHitTestResultData()); |
| |
| auto hitTestData = m_activeContextMenuContextData.webHitTestResultData().value(); |
| |
| switch (item.action()) { |
| #if PLATFORM(COCOA) |
| case ContextMenuItemTagSmartCopyPaste: |
| setSmartInsertDeleteEnabled(!isSmartInsertDeleteEnabled()); |
| return; |
| |
| case ContextMenuItemTagSmartQuotes: |
| TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled); |
| m_process->updateTextCheckerState(); |
| return; |
| |
| case ContextMenuItemTagSmartDashes: |
| TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled); |
| m_process->updateTextCheckerState(); |
| return; |
| |
| case ContextMenuItemTagSmartLinks: |
| TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled); |
| m_process->updateTextCheckerState(); |
| return; |
| |
| case ContextMenuItemTagTextReplacement: |
| TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled); |
| m_process->updateTextCheckerState(); |
| return; |
| |
| case ContextMenuItemTagCorrectSpellingAutomatically: |
| TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled); |
| m_process->updateTextCheckerState(); |
| return; |
| |
| case ContextMenuItemTagShowSubstitutions: |
| TextChecker::toggleSubstitutionsPanelIsShowing(); |
| return; |
| #endif |
| |
| case ContextMenuItemTagDownloadImageToDisk: |
| downloadInfo = { { hitTestData.absoluteImageURL, { } } }; |
| break; |
| |
| case ContextMenuItemTagDownloadLinkToDisk: { |
| downloadInfo = { { hitTestData.absoluteLinkURL, hitTestData.linkSuggestedFilename } }; |
| break; |
| } |
| |
| case ContextMenuItemTagDownloadMediaToDisk: |
| downloadInfo = { { hitTestData.absoluteMediaURL, { } } }; |
| break; |
| |
| case ContextMenuItemTagCheckSpellingWhileTyping: |
| TextChecker::setContinuousSpellCheckingEnabled(!TextChecker::state().isContinuousSpellCheckingEnabled); |
| m_process->updateTextCheckerState(); |
| return; |
| |
| case ContextMenuItemTagCheckGrammarWithSpelling: |
| TextChecker::setGrammarCheckingEnabled(!TextChecker::state().isGrammarCheckingEnabled); |
| m_process->updateTextCheckerState(); |
| return; |
| |
| #if PLATFORM(MAC) |
| case ContextMenuItemTagShowFonts: |
| showFontPanel(); |
| return; |
| case ContextMenuItemTagStyles: |
| showStylesPanel(); |
| return; |
| case ContextMenuItemTagShowColors: |
| showColorPanel(); |
| return; |
| #endif // PLATFORM(MAC) |
| |
| case ContextMenuItemTagShowSpellingPanel: |
| if (!TextChecker::spellingUIIsShowing()) |
| advanceToNextMisspelling(true); |
| TextChecker::toggleSpellingUIIsShowing(); |
| return; |
| |
| case ContextMenuItemTagAddHighlightToNewQuickNote: |
| #if ENABLE(APP_HIGHLIGHTS) |
| createAppHighlightInSelectedRange(CreateNewGroupForHighlight::Yes, HighlightRequestOriginatedInApp::No); |
| #endif |
| return; |
| |
| case ContextMenuItemTagAddHighlightToCurrentQuickNote: |
| #if ENABLE(APP_HIGHLIGHTS) |
| createAppHighlightInSelectedRange(CreateNewGroupForHighlight::No, HighlightRequestOriginatedInApp::No); |
| #endif |
| return; |
| |
| case ContextMenuItemTagLearnSpelling: |
| case ContextMenuItemTagIgnoreSpelling: |
| ++m_pendingLearnOrIgnoreWordMessageCount; |
| break; |
| |
| case ContextMenuItemTagLookUpImage: |
| #if ENABLE(IMAGE_ANALYSIS) |
| handleContextMenuLookUpImage(); |
| #endif |
| return; |
| |
| case ContextMenuItemTagCopySubject: |
| #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) |
| handleContextMenuCopySubject(hitTestData.sourceImageMIMEType); |
| #endif |
| return; |
| |
| default: |
| break; |
| } |
| |
| if (downloadInfo) { |
| auto& download = m_process->processPool().download(m_websiteDataStore, this, URL { downloadInfo->url }, downloadInfo->suggestedFilename); |
| download.setDidStartCallback([this, weakThis = WeakPtr { *this }] (auto* download) { |
| if (!weakThis || !download) |
| return; |
| m_navigationClient->contextMenuDidCreateDownload(*this, *download); |
| }); |
| } |
| |
| platformDidSelectItemFromActiveContextMenu(item); |
| |
| send(Messages::WebPage::DidSelectItemFromActiveContextMenu(item)); |
| } |
| |
| void WebPageProxy::handleContextMenuKeyEvent() |
| { |
| send(Messages::WebPage::ContextMenuForKeyEvent()); |
| } |
| #endif // ENABLE(CONTEXT_MENUS) |
| |
| #if PLATFORM(IOS_FAMILY) |
| void WebPageProxy::didChooseFilesForOpenPanelWithDisplayStringAndIcon(const Vector<String>& fileURLs, const String& displayString, const API::Data* iconData) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| #if ENABLE(SANDBOX_EXTENSIONS) |
| auto sandboxExtensionHandles = SandboxExtension::createReadOnlyHandlesForFiles("WebPageProxy::didChooseFilesForOpenPanelWithDisplayStringAndIcon"_s, fileURLs); |
| send(Messages::WebPage::ExtendSandboxForFilesFromOpenPanel(WTFMove(sandboxExtensionHandles))); |
| #endif |
| |
| SandboxExtension::Handle frontboardServicesSandboxExtension, iconServicesSandboxExtension; |
| auto auditToken = m_process->auditToken(); |
| #if HAVE(FRONTBOARD_SYSTEM_APP_SERVICES) |
| if (auto handle = SandboxExtension::createHandleForMachLookup("com.apple.frontboard.systemappservices"_s, auditToken, SandboxExtension::MachBootstrapOptions::EnableMachBootstrap)) |
| frontboardServicesSandboxExtension = WTFMove(*handle); |
| #endif |
| if (auto handle = SandboxExtension::createHandleForMachLookup("com.apple.iconservices"_s, auditToken, SandboxExtension::MachBootstrapOptions::EnableMachBootstrap)) |
| iconServicesSandboxExtension = WTFMove(*handle); |
| |
| send(Messages::WebPage::DidChooseFilesForOpenPanelWithDisplayStringAndIcon(fileURLs, displayString, iconData ? iconData->dataReference() : IPC::DataReference(), frontboardServicesSandboxExtension, iconServicesSandboxExtension)); |
| |
| m_openPanelResultListener->invalidate(); |
| m_openPanelResultListener = nullptr; |
| } |
| #endif |
| |
| bool WebPageProxy::didChooseFilesForOpenPanelWithImageTranscoding(const Vector<String>& fileURLs, const Vector<String>& allowedMIMETypes) |
| { |
| #if PLATFORM(MAC) |
| auto transcodingMIMEType = WebCore::MIMETypeRegistry::preferredImageMIMETypeForEncoding(allowedMIMETypes, { }); |
| if (transcodingMIMEType.isNull()) |
| return false; |
| |
| auto transcodingURLs = findImagesForTranscoding(fileURLs, allowedMIMETypes); |
| if (transcodingURLs.isEmpty()) |
| return false; |
| |
| auto transcodingUTI = WebCore::UTIFromMIMEType(transcodingMIMEType); |
| auto transcodingExtension = WebCore::MIMETypeRegistry::preferredExtensionForMIMEType(transcodingMIMEType); |
| |
| sharedImageTranscodingQueue().dispatch([this, protectedThis = Ref { *this }, fileURLs = crossThreadCopy(fileURLs), transcodingURLs = crossThreadCopy(WTFMove(transcodingURLs)), transcodingUTI = WTFMove(transcodingUTI).isolatedCopy(), transcodingExtension = WTFMove(transcodingExtension).isolatedCopy()]() mutable { |
| ASSERT(!RunLoop::isMain()); |
| |
| auto transcodedURLs = transcodeImages(transcodingURLs, transcodingUTI, transcodingExtension); |
| ASSERT(transcodingURLs.size() == transcodedURLs.size()); |
| |
| RunLoop::main().dispatch([this, protectedThis = WTFMove(protectedThis), fileURLs = crossThreadCopy(WTFMove(fileURLs)), transcodedURLs = crossThreadCopy(WTFMove(transcodedURLs))]() { |
| #if ENABLE(SANDBOX_EXTENSIONS) |
| Vector<String> sandboxExtensionFiles; |
| for (size_t i = 0, size = fileURLs.size(); i < size; ++i) |
| sandboxExtensionFiles.append(!transcodedURLs[i].isNull() ? transcodedURLs[i] : fileURLs[i]); |
| auto sandboxExtensionHandles = SandboxExtension::createReadOnlyHandlesForFiles("WebPageProxy::didChooseFilesForOpenPanel"_s, sandboxExtensionFiles); |
| send(Messages::WebPage::ExtendSandboxForFilesFromOpenPanel(WTFMove(sandboxExtensionHandles))); |
| #endif |
| send(Messages::WebPage::DidChooseFilesForOpenPanel(fileURLs, transcodedURLs)); |
| }); |
| }); |
| |
| return true; |
| #else |
| UNUSED_PARAM(fileURLs); |
| UNUSED_PARAM(allowedMIMETypes); |
| return false; |
| #endif |
| } |
| |
| void WebPageProxy::didChooseFilesForOpenPanel(const Vector<String>& fileURLs, const Vector<String>& allowedMIMETypes) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| if (!didChooseFilesForOpenPanelWithImageTranscoding(fileURLs, allowedMIMETypes)) { |
| #if ENABLE(SANDBOX_EXTENSIONS) |
| auto sandboxExtensionHandles = SandboxExtension::createReadOnlyHandlesForFiles("WebPageProxy::didChooseFilesForOpenPanel"_s, fileURLs); |
| send(Messages::WebPage::ExtendSandboxForFilesFromOpenPanel(WTFMove(sandboxExtensionHandles))); |
| #endif |
| send(Messages::WebPage::DidChooseFilesForOpenPanel(fileURLs, { })); |
| } |
| |
| m_openPanelResultListener->invalidate(); |
| m_openPanelResultListener = nullptr; |
| } |
| |
| void WebPageProxy::didCancelForOpenPanel() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::DidCancelForOpenPanel()); |
| |
| m_openPanelResultListener->invalidate(); |
| m_openPanelResultListener = nullptr; |
| } |
| |
| void WebPageProxy::advanceToNextMisspelling(bool startBeforeSelection) |
| { |
| send(Messages::WebPage::AdvanceToNextMisspelling(startBeforeSelection)); |
| } |
| |
| void WebPageProxy::changeSpellingToWord(const String& word) |
| { |
| if (word.isEmpty()) |
| return; |
| |
| send(Messages::WebPage::ChangeSpellingToWord(word)); |
| } |
| |
| void WebPageProxy::registerEditCommand(Ref<WebEditCommandProxy>&& commandProxy, UndoOrRedo undoOrRedo) |
| { |
| MESSAGE_CHECK(m_process, commandProxy->commandID()); |
| pageClient().registerEditCommand(WTFMove(commandProxy), undoOrRedo); |
| } |
| |
| void WebPageProxy::addEditCommand(WebEditCommandProxy& command) |
| { |
| m_editCommandSet.add(&command); |
| } |
| |
| void WebPageProxy::removeEditCommand(WebEditCommandProxy& command) |
| { |
| m_editCommandSet.remove(&command); |
| |
| if (!hasRunningProcess()) |
| return; |
| send(Messages::WebPage::DidRemoveEditCommand(command.commandID())); |
| } |
| |
| bool WebPageProxy::canUndo() |
| { |
| return pageClient().canUndoRedo(UndoOrRedo::Undo); |
| } |
| |
| bool WebPageProxy::canRedo() |
| { |
| return pageClient().canUndoRedo(UndoOrRedo::Redo); |
| } |
| |
| SpellDocumentTag WebPageProxy::spellDocumentTag() |
| { |
| if (!m_spellDocumentTag) |
| m_spellDocumentTag = TextChecker::uniqueSpellDocumentTag(this); |
| return m_spellDocumentTag.value(); |
| } |
| |
| #if USE(UNIFIED_TEXT_CHECKING) |
| void WebPageProxy::checkTextOfParagraph(const String& text, OptionSet<TextCheckingType> checkingTypes, int32_t insertionPoint, CompletionHandler<void(Vector<WebCore::TextCheckingResult>&&)>&& completionHandler) |
| { |
| completionHandler(TextChecker::checkTextOfParagraph(spellDocumentTag(), text, insertionPoint, checkingTypes, m_initialCapitalizationEnabled)); |
| } |
| #endif |
| |
| void WebPageProxy::checkSpellingOfString(const String& text, CompletionHandler<void(int32_t misspellingLocation, int32_t misspellingLength)>&& completionHandler) |
| { |
| int32_t misspellingLocation = 0; |
| int32_t misspellingLength = 0; |
| TextChecker::checkSpellingOfString(spellDocumentTag(), text, misspellingLocation, misspellingLength); |
| completionHandler(misspellingLocation, misspellingLength); |
| } |
| |
| void WebPageProxy::checkGrammarOfString(const String& text, CompletionHandler<void(Vector<WebCore::GrammarDetail>&&, int32_t badGrammarLocation, int32_t badGrammarLength)>&& completionHandler) |
| { |
| Vector<GrammarDetail> grammarDetails; |
| int32_t badGrammarLocation = 0; |
| int32_t badGrammarLength = 0; |
| TextChecker::checkGrammarOfString(spellDocumentTag(), text, grammarDetails, badGrammarLocation, badGrammarLength); |
| completionHandler(WTFMove(grammarDetails), badGrammarLocation, badGrammarLength); |
| } |
| |
| void WebPageProxy::spellingUIIsShowing(CompletionHandler<void(bool)>&& completionHandler) |
| { |
| completionHandler(TextChecker::spellingUIIsShowing()); |
| } |
| |
| void WebPageProxy::updateSpellingUIWithMisspelledWord(const String& misspelledWord) |
| { |
| TextChecker::updateSpellingUIWithMisspelledWord(spellDocumentTag(), misspelledWord); |
| } |
| |
| void WebPageProxy::updateSpellingUIWithGrammarString(const String& badGrammarPhrase, const GrammarDetail& grammarDetail) |
| { |
| TextChecker::updateSpellingUIWithGrammarString(spellDocumentTag(), badGrammarPhrase, grammarDetail); |
| } |
| |
| void WebPageProxy::getGuessesForWord(const String& word, const String& context, int32_t insertionPoint, CompletionHandler<void(Vector<String>&&)>&& completionHandler) |
| { |
| Vector<String> guesses; |
| TextChecker::getGuessesForWord(spellDocumentTag(), word, context, insertionPoint, guesses, m_initialCapitalizationEnabled); |
| completionHandler(WTFMove(guesses)); |
| } |
| |
| void WebPageProxy::learnWord(const String& word) |
| { |
| MESSAGE_CHECK(m_process, m_pendingLearnOrIgnoreWordMessageCount); |
| --m_pendingLearnOrIgnoreWordMessageCount; |
| |
| TextChecker::learnWord(spellDocumentTag(), word); |
| } |
| |
| void WebPageProxy::ignoreWord(const String& word) |
| { |
| MESSAGE_CHECK(m_process, m_pendingLearnOrIgnoreWordMessageCount); |
| --m_pendingLearnOrIgnoreWordMessageCount; |
| |
| TextChecker::ignoreWord(spellDocumentTag(), word); |
| } |
| |
| void WebPageProxy::requestCheckingOfString(TextCheckerRequestID requestID, const TextCheckingRequestData& request, int32_t insertionPoint) |
| { |
| TextChecker::requestCheckingOfString(TextCheckerCompletion::create(requestID, request, this), insertionPoint); |
| } |
| |
| void WebPageProxy::didFinishCheckingText(TextCheckerRequestID requestID, const Vector<WebCore::TextCheckingResult>& result) |
| { |
| send(Messages::WebPage::DidFinishCheckingText(requestID, result)); |
| } |
| |
| void WebPageProxy::didCancelCheckingText(TextCheckerRequestID requestID) |
| { |
| send(Messages::WebPage::DidCancelCheckingText(requestID)); |
| } |
| |
| void WebPageProxy::focusFromServiceWorker(CompletionHandler<void()>&& callback) |
| { |
| if (!m_uiClient->focusFromServiceWorker(*this)) { |
| callback(); |
| return; |
| } |
| |
| #if PLATFORM(COCOA) |
| makeFirstResponder(); |
| #endif |
| |
| if (m_activityState.contains(ActivityState::IsVisible)) { |
| callback(); |
| return; |
| } |
| installActivityStateChangeCompletionHandler(WTFMove(callback)); |
| } |
| |
| // Other |
| |
| void WebPageProxy::setFocus(bool focused) |
| { |
| if (focused) |
| m_uiClient->focus(this); |
| else |
| m_uiClient->unfocus(this); |
| } |
| |
| void WebPageProxy::takeFocus(uint8_t direction) |
| { |
| if (m_uiClient->takeFocus(this, (static_cast<FocusDirection>(direction) == FocusDirection::Forward) ? kWKFocusDirectionForward : kWKFocusDirectionBackward)) |
| return; |
| |
| pageClient().takeFocus(static_cast<FocusDirection>(direction)); |
| } |
| |
| void WebPageProxy::setToolTip(const String& toolTip) |
| { |
| if (m_toolTip == toolTip) |
| return; |
| |
| String oldToolTip = m_toolTip; |
| m_toolTip = toolTip; |
| pageClient().toolTipChanged(oldToolTip, m_toolTip); |
| } |
| |
| void WebPageProxy::setCursor(const WebCore::Cursor& cursor) |
| { |
| pageClient().setCursor(cursor); |
| } |
| |
| void WebPageProxy::setCursorHiddenUntilMouseMoves(bool hiddenUntilMouseMoves) |
| { |
| pageClient().setCursorHiddenUntilMouseMoves(hiddenUntilMouseMoves); |
| } |
| |
| void WebPageProxy::didReceiveEvent(uint32_t opaqueType, bool handled) |
| { |
| WebEvent::Type type = static_cast<WebEvent::Type>(opaqueType); |
| |
| switch (type) { |
| case WebEvent::NoType: |
| case WebEvent::MouseMove: |
| case WebEvent::Wheel: |
| break; |
| |
| case WebEvent::MouseDown: |
| case WebEvent::MouseUp: |
| case WebEvent::MouseForceChanged: |
| case WebEvent::MouseForceDown: |
| case WebEvent::MouseForceUp: |
| case WebEvent::KeyDown: |
| case WebEvent::KeyUp: |
| case WebEvent::RawKeyDown: |
| case WebEvent::Char: |
| #if ENABLE(TOUCH_EVENTS) |
| case WebEvent::TouchStart: |
| case WebEvent::TouchMove: |
| case WebEvent::TouchEnd: |
| case WebEvent::TouchCancel: |
| #endif |
| #if ENABLE(MAC_GESTURE_EVENTS) |
| case WebEvent::GestureStart: |
| case WebEvent::GestureChange: |
| case WebEvent::GestureEnd: |
| #endif |
| m_process->stopResponsivenessTimer(); |
| break; |
| } |
| |
| switch (type) { |
| case WebEvent::NoType: |
| break; |
| case WebEvent::MouseForceChanged: |
| case WebEvent::MouseForceDown: |
| case WebEvent::MouseForceUp: |
| case WebEvent::MouseMove: |
| case WebEvent::MouseDown: |
| case WebEvent::MouseUp: { |
| LOG(MouseHandling, "WebPageProxy::didReceiveEvent: %s (queue size %zu)", webMouseEventTypeString(type), m_mouseEventQueue.size()); |
| |
| // Retire the last sent event now that WebProcess is done handling it. |
| MESSAGE_CHECK(m_process, !m_mouseEventQueue.isEmpty()); |
| auto event = m_mouseEventQueue.takeFirst(); |
| MESSAGE_CHECK(m_process, type == event.type()); |
| |
| if (!m_mouseEventQueue.isEmpty()) { |
| LOG(MouseHandling, " UIProcess: handling a queued mouse event from didReceiveEvent"); |
| processNextQueuedMouseEvent(); |
| } else { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->mouseEventsFlushedForPage(*this); |
| didFinishProcessingAllPendingMouseEvents(); |
| } |
| break; |
| } |
| |
| case WebEvent::Wheel: { |
| MESSAGE_CHECK(m_process, wheelEventCoalescer().hasEventsBeingProcessed()); |
| auto oldestProcessedEvent = wheelEventCoalescer().takeOldestEventBeingProcessed(); |
| |
| // FIXME: Dispatch additional events to the didNotHandleWheelEvent client function. |
| if (!handled) { |
| m_uiClient->didNotHandleWheelEvent(this, oldestProcessedEvent); |
| pageClient().wheelEventWasNotHandledByWebCore(oldestProcessedEvent); |
| } |
| |
| if (auto eventToSend = wheelEventCoalescer().nextEventToDispatch()) |
| sendWheelEvent(*eventToSend); |
| else if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->wheelEventsFlushedForPage(*this); |
| break; |
| } |
| |
| case WebEvent::KeyDown: |
| case WebEvent::KeyUp: |
| case WebEvent::RawKeyDown: |
| case WebEvent::Char: { |
| LOG(KeyHandling, "WebPageProxy::didReceiveEvent: %s (queue empty %d)", webKeyboardEventTypeString(type), m_keyEventQueue.isEmpty()); |
| |
| MESSAGE_CHECK(m_process, !m_keyEventQueue.isEmpty()); |
| auto event = m_keyEventQueue.takeFirst(); |
| MESSAGE_CHECK(m_process, type == event.type()); |
| |
| #if PLATFORM(WIN) |
| if (!handled && type == WebEvent::RawKeyDown) |
| dispatchPendingCharEvents(event); |
| #endif |
| |
| bool canProcessMoreKeyEvents = !m_keyEventQueue.isEmpty(); |
| if (canProcessMoreKeyEvents) { |
| LOG(KeyHandling, " UI process: sent keyEvent from didReceiveEvent"); |
| send(Messages::WebPage::KeyEvent(m_keyEventQueue.first())); |
| } |
| |
| // The call to doneWithKeyEvent may close this WebPage. |
| // Protect against this being destroyed. |
| Ref<WebPageProxy> protect(*this); |
| |
| pageClient().doneWithKeyEvent(event, handled); |
| if (!handled) |
| m_uiClient->didNotHandleKeyEvent(this, event); |
| |
| // Notify the session after -[NSApp sendEvent:] has a crack at turning the event into an action. |
| if (!canProcessMoreKeyEvents) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->keyboardEventsFlushedForPage(*this); |
| } |
| break; |
| } |
| #if ENABLE(MAC_GESTURE_EVENTS) |
| case WebEvent::GestureStart: |
| case WebEvent::GestureChange: |
| case WebEvent::GestureEnd: { |
| MESSAGE_CHECK(m_process, !m_gestureEventQueue.isEmpty()); |
| auto event = m_gestureEventQueue.takeFirst(); |
| MESSAGE_CHECK(m_process, type == event.type()); |
| |
| if (!handled) |
| pageClient().gestureEventWasNotHandledByWebCore(event); |
| break; |
| } |
| #endif |
| #if ENABLE(IOS_TOUCH_EVENTS) |
| case WebEvent::TouchStart: |
| case WebEvent::TouchMove: |
| case WebEvent::TouchEnd: |
| case WebEvent::TouchCancel: |
| break; |
| #elif ENABLE(TOUCH_EVENTS) |
| case WebEvent::TouchStart: |
| case WebEvent::TouchMove: |
| case WebEvent::TouchEnd: |
| case WebEvent::TouchCancel: { |
| MESSAGE_CHECK(m_process, !m_touchEventQueue.isEmpty()); |
| auto queuedEvents = m_touchEventQueue.takeFirst(); |
| MESSAGE_CHECK(m_process, type == queuedEvents.forwardedEvent.type()); |
| |
| pageClient().doneWithTouchEvent(queuedEvents.forwardedEvent, handled); |
| for (size_t i = 0; i < queuedEvents.deferredTouchEvents.size(); ++i) { |
| bool isEventHandled = false; |
| pageClient().doneWithTouchEvent(queuedEvents.deferredTouchEvents.at(i), isEventHandled); |
| } |
| break; |
| } |
| #endif |
| } |
| } |
| |
| void WebPageProxy::editorStateChanged(const EditorState& editorState) |
| { |
| if (updateEditorState(editorState)) |
| dispatchDidUpdateEditorState(); |
| } |
| |
| bool WebPageProxy::updateEditorState(const EditorState& newEditorState) |
| { |
| if (newEditorState.identifier < m_editorState.identifier) |
| return false; |
| |
| auto oldEditorState = std::exchange(m_editorState, newEditorState); |
| didUpdateEditorState(oldEditorState, newEditorState); |
| return true; |
| } |
| |
| #if !PLATFORM(IOS_FAMILY) |
| |
| void WebPageProxy::dispatchDidUpdateEditorState() |
| { |
| } |
| |
| #endif |
| |
| inline API::DiagnosticLoggingClient* WebPageProxy::effectiveDiagnosticLoggingClient(ShouldSample shouldSample) |
| { |
| // Diagnostic logging is disabled for ephemeral sessions for privacy reasons. |
| if (sessionID().isEphemeral()) |
| return nullptr; |
| |
| return DiagnosticLoggingClient::shouldLogAfterSampling(shouldSample) ? diagnosticLoggingClient() : nullptr; |
| } |
| |
| void WebPageProxy::logDiagnosticMessage(const String& message, const String& description, WebCore::ShouldSample shouldSample) |
| { |
| auto* effectiveClient = effectiveDiagnosticLoggingClient(shouldSample); |
| if (!effectiveClient) |
| return; |
| |
| effectiveClient->logDiagnosticMessage(this, message, description); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageFromWebProcess(const String& message, const String& description, WebCore::ShouldSample shouldSample) |
| { |
| MESSAGE_CHECK(m_process, message.isAllASCII()); |
| |
| logDiagnosticMessage(message, description, shouldSample); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithResult(const String& message, const String& description, uint32_t result, WebCore::ShouldSample shouldSample) |
| { |
| auto* effectiveClient = effectiveDiagnosticLoggingClient(shouldSample); |
| if (!effectiveClient) |
| return; |
| |
| effectiveClient->logDiagnosticMessageWithResult(this, message, description, static_cast<WebCore::DiagnosticLoggingResultType>(result)); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithResultFromWebProcess(const String& message, const String& description, uint32_t result, WebCore::ShouldSample shouldSample) |
| { |
| MESSAGE_CHECK(m_process, message.isAllASCII()); |
| |
| logDiagnosticMessageWithResult(message, description, result, shouldSample); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithValue(const String& message, const String& description, double value, unsigned significantFigures, ShouldSample shouldSample) |
| { |
| auto* effectiveClient = effectiveDiagnosticLoggingClient(shouldSample); |
| if (!effectiveClient) |
| return; |
| |
| effectiveClient->logDiagnosticMessageWithValue(this, message, description, String::numberToStringFixedPrecision(value, significantFigures)); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithValueFromWebProcess(const String& message, const String& description, double value, unsigned significantFigures, ShouldSample shouldSample) |
| { |
| MESSAGE_CHECK(m_process, message.isAllASCII()); |
| |
| logDiagnosticMessageWithValue(message, description, value, significantFigures, shouldSample); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithEnhancedPrivacy(const String& message, const String& description, ShouldSample shouldSample) |
| { |
| auto* effectiveClient = effectiveDiagnosticLoggingClient(shouldSample); |
| if (!effectiveClient) |
| return; |
| |
| effectiveClient->logDiagnosticMessageWithEnhancedPrivacy(this, message, description); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithEnhancedPrivacyFromWebProcess(const String& message, const String& description, WebCore::ShouldSample shouldSample) |
| { |
| MESSAGE_CHECK(m_process, message.isAllASCII()); |
| |
| logDiagnosticMessageWithEnhancedPrivacy(message, description, shouldSample); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithValueDictionary(const String& message, const String& description, const WebCore::DiagnosticLoggingClient::ValueDictionary& valueDictionary, WebCore::ShouldSample shouldSample) |
| { |
| auto* effectiveClient = effectiveDiagnosticLoggingClient(shouldSample); |
| if (!effectiveClient) |
| return; |
| |
| auto apiDictionary = API::Dictionary::create(); |
| |
| for (auto& keyValuePair : valueDictionary) { |
| apiDictionary->add(keyValuePair.key, WTF::switchOn(keyValuePair.value, |
| [](const String& value) -> Ref<Object> { return API::String::create(value); }, |
| [](uint64_t value) -> Ref<Object> { return API::UInt64::create(value); }, |
| [](int64_t value) -> Ref<Object> { return API::Int64::create(value); }, |
| [](bool value) -> Ref<Object> { return API::Boolean::create(value); }, |
| [](double value) -> Ref<Object> { return API::Double::create(value); } |
| )); |
| } |
| |
| effectiveClient->logDiagnosticMessageWithValueDictionary(this, message, description, WTFMove(apiDictionary)); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithValueDictionaryFromWebProcess(const String& message, const String& description, const WebCore::DiagnosticLoggingClient::ValueDictionary& valueDictionary, WebCore::ShouldSample shouldSample) |
| { |
| MESSAGE_CHECK(m_process, message.isAllASCII()); |
| |
| logDiagnosticMessageWithValueDictionary(message, description, valueDictionary, shouldSample); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithDomain(const String& message, WebCore::DiagnosticLoggingDomain domain) |
| { |
| auto* effectiveClient = effectiveDiagnosticLoggingClient(ShouldSample::No); |
| if (!effectiveClient) |
| return; |
| |
| effectiveClient->logDiagnosticMessageWithDomain(this, message, domain); |
| } |
| |
| void WebPageProxy::logDiagnosticMessageWithDomainFromWebProcess(const String& message, WebCore::DiagnosticLoggingDomain domain) |
| { |
| MESSAGE_CHECK(m_process, message.isAllASCII()); |
| |
| logDiagnosticMessageWithDomain(message, domain); |
| } |
| |
| void WebPageProxy::logScrollingEvent(uint32_t eventType, MonotonicTime timestamp, uint64_t data) |
| { |
| PerformanceLoggingClient::ScrollingEvent event = static_cast<PerformanceLoggingClient::ScrollingEvent>(eventType); |
| |
| switch (event) { |
| case PerformanceLoggingClient::ScrollingEvent::LoggingEnabled: |
| WTFLogAlways("SCROLLING: ScrollingPerformanceTestingEnabled\n"); |
| break; |
| case PerformanceLoggingClient::ScrollingEvent::ExposedTilelessArea: |
| WTFLogAlways("SCROLLING: Exposed tileless area. Time: %f Unfilled Pixels: %llu\n", timestamp.secondsSinceEpoch().value(), (unsigned long long)data); |
| break; |
| case PerformanceLoggingClient::ScrollingEvent::FilledTile: |
| WTFLogAlways("SCROLLING: Filled visible fresh tile. Time: %f Unfilled Pixels: %llu\n", timestamp.secondsSinceEpoch().value(), (unsigned long long)data); |
| break; |
| case PerformanceLoggingClient::ScrollingEvent::SwitchedScrollingMode: |
| if (data) |
| WTFLogAlways("SCROLLING: Switching to main-thread scrolling mode. Time: %f Reason(s): %s\n", timestamp.secondsSinceEpoch().value(), PerformanceLoggingClient::synchronousScrollingReasonsAsString(OptionSet<SynchronousScrollingReason>::fromRaw(data)).utf8().data()); |
| else |
| WTFLogAlways("SCROLLING: Switching to threaded scrolling mode. Time: %f\n", timestamp.secondsSinceEpoch().value()); |
| break; |
| } |
| } |
| |
| void WebPageProxy::focusedFrameChanged(const std::optional<FrameIdentifier>& frameID) |
| { |
| if (!frameID) { |
| m_focusedFrame = nullptr; |
| return; |
| } |
| |
| WebFrameProxy* frame = m_process->webFrame(*frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| m_focusedFrame = frame; |
| } |
| |
| void WebPageProxy::processDidBecomeUnresponsive() |
| { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Process, "processDidBecomeUnresponsive:"); |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| updateBackingStoreDiscardableState(); |
| |
| m_navigationClient->processDidBecomeUnresponsive(*this); |
| } |
| |
| void WebPageProxy::processDidBecomeResponsive() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Process, "processDidBecomeResponsive:"); |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| updateBackingStoreDiscardableState(); |
| |
| m_navigationClient->processDidBecomeResponsive(*this); |
| } |
| |
| void WebPageProxy::willChangeProcessIsResponsive() |
| { |
| m_pageLoadState.willChangeProcessIsResponsive(); |
| } |
| |
| void WebPageProxy::didChangeProcessIsResponsive() |
| { |
| m_pageLoadState.didChangeProcessIsResponsive(); |
| } |
| |
| String WebPageProxy::currentURL() const |
| { |
| String url = m_pageLoadState.activeURL(); |
| if (url.isEmpty() && m_backForwardList->currentItem()) |
| url = m_backForwardList->currentItem()->url(); |
| return url; |
| } |
| |
| URL WebPageProxy::currentResourceDirectoryURL() const |
| { |
| auto resourceDirectoryURL = m_pageLoadState.resourceDirectoryURL(); |
| if (!resourceDirectoryURL.isEmpty()) |
| return resourceDirectoryURL; |
| if (auto* item = m_backForwardList->currentItem()) |
| return item->resourceDirectoryURL(); |
| return { }; |
| } |
| |
| void WebPageProxy::resetStateAfterProcessTermination(ProcessTerminationReason reason) |
| { |
| if (reason != ProcessTerminationReason::NavigationSwap) |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Process, "processDidTerminate: (pid %d), reason=%{public}s", processIdentifier(), processTerminationReasonToString(reason)); |
| |
| ASSERT(m_hasRunningProcess); |
| |
| #if PLATFORM(IOS_FAMILY) |
| if (m_process->isUnderMemoryPressure()) { |
| String domain = WebCore::topPrivatelyControlledDomain(URL({ }, currentURL()).host().toString()); |
| if (!domain.isEmpty()) |
| logDiagnosticMessageWithEnhancedPrivacy(WebCore::DiagnosticLoggingKeys::domainCausingJetsamKey(), domain, WebCore::ShouldSample::No); |
| } |
| #endif |
| |
| resetStateAfterProcessExited(reason); |
| stopAllURLSchemeTasks(m_process.ptr()); |
| #if ENABLE(UI_PROCESS_PDF_HUD) |
| pageClient().removeAllPDFHUDs(); |
| #endif |
| |
| // For bringup of process swapping, NavigationSwap termination will not go out to clients. |
| // If it does *during* process swapping, and the client triggers a reload, that causes bizarre WebKit re-entry. |
| // FIXME: This might have to change |
| if (reason != ProcessTerminationReason::NavigationSwap) |
| navigationState().clearAllNavigations(); |
| |
| if (m_controlledByAutomation) { |
| if (auto* automationSession = process().processPool().automationSession()) |
| automationSession->terminate(); |
| } |
| } |
| |
| void WebPageProxy::provisionalProcessDidTerminate() |
| { |
| ASSERT(m_provisionalPage); |
| m_provisionalPage = nullptr; |
| } |
| |
| static bool shouldReloadAfterProcessTermination(ProcessTerminationReason reason) |
| { |
| switch (reason) { |
| case ProcessTerminationReason::ExceededMemoryLimit: |
| case ProcessTerminationReason::ExceededCPULimit: |
| case ProcessTerminationReason::RequestedByNetworkProcess: |
| case ProcessTerminationReason::RequestedByGPUProcess: |
| case ProcessTerminationReason::Crash: |
| case ProcessTerminationReason::Unresponsive: |
| return true; |
| case ProcessTerminationReason::ExceededProcessCountLimit: |
| case ProcessTerminationReason::NavigationSwap: |
| case ProcessTerminationReason::IdleExit: |
| case ProcessTerminationReason::RequestedByClient: |
| break; |
| } |
| return false; |
| } |
| |
| void WebPageProxy::dispatchProcessDidTerminate(ProcessTerminationReason reason) |
| { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Loading, "dispatchProcessDidTerminate: reason=%{public}s", processTerminationReasonToString(reason)); |
| |
| bool handledByClient = false; |
| if (m_loaderClient) |
| handledByClient = reason != ProcessTerminationReason::RequestedByClient && m_loaderClient->processDidCrash(*this); |
| else |
| handledByClient = m_navigationClient->processDidTerminate(*this, reason); |
| |
| if (!handledByClient && shouldReloadAfterProcessTermination(reason)) { |
| // We delay the view reload until it becomes visible. |
| if (isViewVisible()) |
| tryReloadAfterProcessTermination(); |
| else { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Loading, "dispatchProcessDidTerminate: Not eagerly reloading the view because it is not currently visible"); |
| m_shouldReloadDueToCrashWhenVisible = true; |
| } |
| } |
| } |
| |
| void WebPageProxy::tryReloadAfterProcessTermination() |
| { |
| m_resetRecentCrashCountTimer.stop(); |
| |
| if (++m_recentCrashCount > maximumWebProcessRelaunchAttempts) { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(Process, "tryReloadAfterProcessTermination: process crashed and the client did not handle it, not reloading the page because we reached the maximum number of attempts"); |
| m_recentCrashCount = 0; |
| return; |
| } |
| WEBPAGEPROXY_RELEASE_LOG(Process, "tryReloadAfterProcessTermination: process crashed and the client did not handle it, reloading the page"); |
| reload(ReloadOption::ExpiredOnly); |
| } |
| |
| void WebPageProxy::resetRecentCrashCountSoon() |
| { |
| m_resetRecentCrashCountTimer.startOneShot(resetRecentCrashCountDelay); |
| } |
| |
| void WebPageProxy::resetRecentCrashCount() |
| { |
| m_recentCrashCount = 0; |
| } |
| |
| void WebPageProxy::stopAllURLSchemeTasks(WebProcessProxy* process) |
| { |
| for (auto& handler : copyToVectorOf<Ref<WebURLSchemeHandler>>(m_urlSchemeHandlersByScheme.values())) |
| handler->stopAllTasksForPage(*this, process); |
| } |
| |
| void WebPageProxy::resetState(ResetStateReason resetStateReason) |
| { |
| m_mainFrame = nullptr; |
| m_focusedFrame = nullptr; |
| m_suspendedPageKeptToPreventFlashing = nullptr; |
| m_lastSuspendedPage = nullptr; |
| |
| #if PLATFORM(COCOA) |
| m_scrollingPerformanceData = nullptr; |
| #endif |
| |
| if (m_drawingArea) { |
| #if PLATFORM(COCOA) |
| if (resetStateReason == ResetStateReason::NavigationSwap && is<RemoteLayerTreeDrawingAreaProxy>(*m_drawingArea)) { |
| // Keep layers around in frozen state to avoid flashing during process swaps. |
| m_frozenRemoteLayerTreeHost = downcast<RemoteLayerTreeDrawingAreaProxy>(*m_drawingArea).detachRemoteLayerTreeHost(); |
| } |
| #endif |
| m_drawingArea = nullptr; |
| } |
| closeOverlayedViews(); |
| |
| m_inspector->reset(); |
| |
| #if ENABLE(FULLSCREEN_API) |
| if (m_fullScreenManager) { |
| m_fullScreenManager->close(); |
| m_fullScreenManager = nullptr; |
| } |
| #endif |
| |
| #if ENABLE(MEDIA_USAGE) |
| if (m_mediaUsageManager) |
| m_mediaUsageManager->reset(); |
| #endif |
| |
| #if HAVE(VISIBILITY_PROPAGATION_VIEW) |
| if (resetStateReason != ResetStateReason::NavigationSwap) |
| m_contextIDForVisibilityPropagationInWebProcess = 0; |
| #endif |
| |
| if (resetStateReason != ResetStateReason::NavigationSwap) |
| callLoadCompletionHandlersIfNecessary(false); |
| |
| if (m_openPanelResultListener) { |
| m_openPanelResultListener->invalidate(); |
| m_openPanelResultListener = nullptr; |
| } |
| |
| #if ENABLE(TOUCH_EVENTS) |
| m_touchAndPointerEventTracking.reset(); |
| #endif |
| |
| #if ENABLE(GEOLOCATION) |
| m_geolocationPermissionRequestManager.invalidateRequests(); |
| #endif |
| |
| setToolTip({ }); |
| |
| m_mainFrameHasHorizontalScrollbar = false; |
| m_mainFrameHasVerticalScrollbar = false; |
| |
| m_mainFramePinnedState = { true, true, true, true }; |
| |
| m_visibleScrollerThumbRect = IntRect(); |
| |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| if (m_playbackSessionManager) { |
| m_playbackSessionManager->invalidate(); |
| m_playbackSessionManager = nullptr; |
| } |
| if (m_videoFullscreenManager) { |
| m_videoFullscreenManager->invalidate(); |
| m_videoFullscreenManager = nullptr; |
| } |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) |
| m_firstLayerTreeTransactionIdAfterDidCommitLoad = { }; |
| m_lastVisibleContentRectUpdate = { }; |
| m_hasNetworkRequestsOnSuspended = false; |
| m_isKeyboardAnimatingIn = false; |
| m_isScrollingOrZooming = false; |
| m_lastObservedStateWasBackground = false; |
| #endif |
| |
| #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS_FAMILY) |
| pageClient().mediaSessionManager().removeAllPlaybackTargetPickerClients(*this); |
| #endif |
| |
| #if ENABLE(APPLE_PAY) |
| m_paymentCoordinator = nullptr; |
| #endif |
| |
| #if USE(SYSTEM_PREVIEW) |
| m_systemPreviewController = nullptr; |
| #endif |
| |
| #if ENABLE(ARKIT_INLINE_PREVIEW) |
| m_modelElementController = nullptr; |
| #endif |
| |
| #if ENABLE(WEB_AUTHN) |
| m_credentialsMessenger = nullptr; |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) && ENABLE(DEVICE_ORIENTATION) |
| m_webDeviceOrientationUpdateProviderProxy = nullptr; |
| #endif |
| |
| for (auto& editCommand : std::exchange(m_editCommandSet, { })) |
| editCommand->invalidate(); |
| |
| m_activePopupMenu = nullptr; |
| |
| updatePlayingMediaDidChange(MediaProducer::IsNotPlaying); |
| #if ENABLE(MEDIA_STREAM) |
| m_userMediaPermissionRequestManager = nullptr; |
| #endif |
| |
| #if ENABLE(POINTER_LOCK) |
| requestPointerUnlock(); |
| #endif |
| |
| #if ENABLE(SPEECH_SYNTHESIS) |
| resetSpeechSynthesizer(); |
| #endif |
| |
| #if ENABLE(WEB_AUTHN) |
| m_websiteDataStore->authenticatorManager().cancelRequest(m_webPageID, std::nullopt); |
| #endif |
| |
| m_speechRecognitionPermissionManager = nullptr; |
| |
| #if ENABLE(WEBXR) && !USE(OPENXR) |
| if (m_xrSystem) { |
| m_xrSystem->invalidate(); |
| m_xrSystem = nullptr; |
| } |
| #endif |
| |
| #if ENABLE(MOMENTUM_EVENT_DISPATCHER) |
| m_lastSentScrollingAccelerationCurve = std::nullopt; |
| #endif |
| } |
| |
| void WebPageProxy::resetStateAfterProcessExited(ProcessTerminationReason terminationReason) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| PageClientProtector protector(pageClient()); |
| |
| #if ASSERT_ENABLED |
| // FIXME: It's weird that resetStateAfterProcessExited() is called even though the process is launching. |
| if (terminationReason != ProcessTerminationReason::NavigationSwap) |
| ASSERT(m_process->state() == WebProcessProxy::State::Launching || m_process->state() == WebProcessProxy::State::Terminated); |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) |
| m_waitingForPostLayoutEditorStateUpdateAfterFocusingElement = false; |
| m_isVisibleActivity = nullptr; |
| m_isAudibleActivity = nullptr; |
| m_isCapturingActivity = nullptr; |
| m_openingAppLinkActivity = nullptr; |
| #endif |
| |
| m_pageIsUserObservableCount = nullptr; |
| m_visiblePageToken = nullptr; |
| |
| m_hasRunningProcess = false; |
| m_areActiveDOMObjectsAndAnimationsSuspended = false; |
| #if ENABLE(SERVICE_WORKER) |
| m_isServiceWorkerPage = false; |
| #endif |
| |
| m_userScriptsNotified = false; |
| m_hasActiveAnimatedScroll = false; |
| m_registeredForFullSpeedUpdates = false; |
| |
| m_editorState = EditorState(); |
| m_cachedFontAttributesAtSelectionStart.reset(); |
| |
| if (terminationReason != ProcessTerminationReason::NavigationSwap) |
| m_provisionalPage = nullptr; |
| |
| if (terminationReason == ProcessTerminationReason::NavigationSwap) |
| pageClient().processWillSwap(); |
| else |
| pageClient().processDidExit(); |
| |
| pageClient().clearAllEditCommands(); |
| |
| #if PLATFORM(COCOA) |
| WebPasteboardProxy::singleton().revokeAccess(m_process.get()); |
| #endif |
| |
| auto resetStateReason = terminationReason == ProcessTerminationReason::NavigationSwap ? ResetStateReason::NavigationSwap : ResetStateReason::WebProcessExited; |
| resetState(resetStateReason); |
| |
| m_pendingLearnOrIgnoreWordMessageCount = 0; |
| |
| // Can't expect DidReceiveEvent notifications from a crashed web process. |
| m_mouseEventQueue.clear(); |
| m_keyEventQueue.clear(); |
| if (m_wheelEventCoalescer) |
| m_wheelEventCoalescer->clear(); |
| |
| #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) |
| m_touchEventQueue.clear(); |
| #endif |
| |
| #if ENABLE(ATTACHMENT_ELEMENT) |
| invalidateAllAttachments(); |
| #endif |
| |
| #if ENABLE(ASYNC_SCROLLING) && PLATFORM(COCOA) |
| if (m_scrollingCoordinatorProxy) |
| m_scrollingCoordinatorProxy->resetStateAfterProcessExited(); |
| #endif |
| |
| if (terminationReason != ProcessTerminationReason::NavigationSwap) { |
| PageLoadState::Transaction transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.reset(transaction); |
| } |
| |
| updatePlayingMediaDidChange(MediaProducer::IsNotPlaying); |
| |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| m_fullscreenVideoTextRecognitionTimer.stop(); |
| m_currentFullscreenVideoSessionIdentifier = std::nullopt; |
| #endif |
| |
| // FIXME: <rdar://problem/38676604> In case of process swaps, the old process should gracefully suspend instead of terminating. |
| m_process->processTerminated(); |
| } |
| |
| #if PLATFORM(COCOA) |
| |
| static Span<const ASCIILiteral> gpuIOKitClasses() |
| { |
| static constexpr std::array services { |
| #if PLATFORM(IOS_FAMILY) |
| "AGXDeviceUserClient"_s, |
| "IOGPU"_s, |
| "IOSurfaceRootUserClient"_s, |
| #endif |
| #if PLATFORM(MAC) || PLATFORM(MACCATALYST) |
| "AGPMClient"_s, |
| "AppleGraphicsControlClient"_s, |
| "AppleGraphicsPolicyClient"_s, |
| "AppleIntelMEUserClient"_s, |
| "AppleMGPUPowerControlClient"_s, |
| "AppleSNBFBUserClient"_s, |
| "AppleUpstreamUserClient"_s, |
| "AudioAUUC"_s, |
| "IOAccelerationUserClient"_s, |
| "IOAccelerator"_s, |
| "IOAudioControlUserClient"_s, |
| "IOAudioEngineUserClient"_s, |
| "IOSurfaceRootUserClient"_s, |
| #endif |
| // FIXME: Is this also needed in PLATFORM(MACCATALYST)? |
| #if PLATFORM(MAC) && CPU(ARM64) |
| "IOMobileFramebufferUserClient"_s, |
| #endif |
| #if (PLATFORM(MAC) && CPU(ARM64)) || PLATFORM(IOS_FAMILY) |
| "IOSurfaceAcceleratorClient"_s, |
| #endif |
| }; |
| return services; |
| } |
| |
| static Span<const ASCIILiteral> gpuMachServices() |
| { |
| static constexpr std::array services { |
| "com.apple.MTLCompilerService"_s, |
| #if PLATFORM(MAC) || PLATFORM(MACCATALYST) |
| "com.apple.cvmsServ"_s, |
| #endif |
| }; |
| return services; |
| } |
| |
| #endif // PLATFORM(COCOA) |
| |
| WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& process, DrawingAreaProxy& drawingArea, RefPtr<API::WebsitePolicies>&& websitePolicies) |
| { |
| WebPageCreationParameters parameters; |
| |
| parameters.processDisplayName = configuration().processDisplayName(); |
| |
| parameters.viewSize = pageClient().viewSize(); |
| parameters.activityState = m_activityState; |
| parameters.drawingAreaType = drawingArea.type(); |
| parameters.drawingAreaIdentifier = drawingArea.identifier(); |
| parameters.webPageProxyIdentifier = m_identifier; |
| parameters.store = preferencesStore(); |
| parameters.pageGroupData = m_pageGroup->data(); |
| parameters.isEditable = m_isEditable; |
| parameters.underlayColor = m_underlayColor; |
| parameters.useFixedLayout = m_useFixedLayout; |
| parameters.fixedLayoutSize = m_fixedLayoutSize; |
| parameters.defaultUnobscuredSize = m_defaultUnobscuredSize; |
| parameters.minimumUnobscuredSize = m_minimumUnobscuredSize; |
| parameters.maximumUnobscuredSize = m_maximumUnobscuredSize; |
| parameters.viewExposedRect = m_viewExposedRect; |
| parameters.alwaysShowsHorizontalScroller = m_alwaysShowsHorizontalScroller; |
| parameters.alwaysShowsVerticalScroller = m_alwaysShowsVerticalScroller; |
| parameters.suppressScrollbarAnimations = m_suppressScrollbarAnimations; |
| parameters.paginationMode = m_paginationMode; |
| parameters.paginationBehavesLikeColumns = m_paginationBehavesLikeColumns; |
| parameters.pageLength = m_pageLength; |
| parameters.gapBetweenPages = m_gapBetweenPages; |
| parameters.paginationLineGridEnabled = m_paginationLineGridEnabled; |
| parameters.userAgent = userAgent(); |
| parameters.itemStatesWereRestoredByAPIRequest = m_sessionStateWasRestoredByAPIRequest; |
| parameters.itemStates = m_backForwardList->itemStates(); |
| parameters.visitedLinkTableID = m_visitedLinkStore->identifier(); |
| parameters.canRunBeforeUnloadConfirmPanel = m_uiClient->canRunBeforeUnloadConfirmPanel(); |
| parameters.canRunModal = m_canRunModal; |
| parameters.deviceScaleFactor = deviceScaleFactor(); |
| parameters.viewScaleFactor = m_viewScaleFactor; |
| parameters.textZoomFactor = m_textZoomFactor; |
| parameters.pageZoomFactor = m_pageZoomFactor; |
| parameters.topContentInset = m_topContentInset; |
| parameters.mediaVolume = m_mediaVolume; |
| parameters.muted = m_mutedState; |
| parameters.openedByDOM = m_openedByDOM; |
| parameters.mayStartMediaWhenInWindow = m_mayStartMediaWhenInWindow; |
| parameters.mediaPlaybackIsSuspended = m_mediaPlaybackIsSuspended; |
| parameters.minimumSizeForAutoLayout = m_minimumSizeForAutoLayout; |
| parameters.sizeToContentAutoSizeMaximumSize = m_sizeToContentAutoSizeMaximumSize; |
| parameters.autoSizingShouldExpandToViewHeight = m_autoSizingShouldExpandToViewHeight; |
| parameters.viewportSizeForCSSViewportUnits = m_viewportSizeForCSSViewportUnits; |
| parameters.scrollPinningBehavior = m_scrollPinningBehavior; |
| if (m_scrollbarOverlayStyle) |
| parameters.scrollbarOverlayStyle = m_scrollbarOverlayStyle.value(); |
| else |
| parameters.scrollbarOverlayStyle = std::nullopt; |
| parameters.backgroundExtendsBeyondPage = m_backgroundExtendsBeyondPage; |
| parameters.layerHostingMode = m_layerHostingMode; |
| parameters.controlledByAutomation = m_controlledByAutomation; |
| parameters.useDarkAppearance = useDarkAppearance(); |
| parameters.useElevatedUserInterfaceLevel = useElevatedUserInterfaceLevel(); |
| #if PLATFORM(MAC) |
| parameters.colorSpace = pageClient().colorSpace(); |
| parameters.useSystemAppearance = m_useSystemAppearance; |
| #endif |
| |
| #if ENABLE(META_VIEWPORT) |
| parameters.ignoresViewportScaleLimits = m_forceAlwaysUserScalable; |
| parameters.viewportConfigurationViewLayoutSize = m_viewportConfigurationViewLayoutSize; |
| parameters.viewportConfigurationLayoutSizeScaleFactor = m_viewportConfigurationLayoutSizeScaleFactor; |
| parameters.viewportConfigurationMinimumEffectiveDeviceWidth = m_viewportConfigurationMinimumEffectiveDeviceWidth; |
| parameters.overrideViewportArguments = m_overrideViewportArguments; |
| #endif |
| |
| #if PLATFORM(IOS_FAMILY) |
| parameters.screenSize = screenSize(); |
| parameters.availableScreenSize = availableScreenSize(); |
| parameters.overrideScreenSize = overrideScreenSize(); |
| parameters.textAutosizingWidth = textAutosizingWidth(); |
| parameters.mimeTypesWithCustomContentProviders = pageClient().mimeTypesWithCustomContentProviders(); |
| parameters.deviceOrientation = m_deviceOrientation; |
| parameters.keyboardIsAttached = isInHardwareKeyboardMode(); |
| parameters.canShowWhileLocked = m_configuration->canShowWhileLocked(); |
| #endif |
| |
| #if PLATFORM(COCOA) |
| parameters.smartInsertDeleteEnabled = m_isSmartInsertDeleteEnabled; |
| parameters.additionalSupportedImageTypes = m_configuration->additionalSupportedImageTypes(); |
| |
| if (!preferences().useGPUProcessForMediaEnabled() |
| || (!preferences().captureVideoInGPUProcessEnabled() && !preferences().captureVideoInUIProcessEnabled()) |
| || (!preferences().captureAudioInGPUProcessEnabled() && !preferences().captureAudioInUIProcessEnabled()) |
| || !preferences().webRTCPlatformCodecsInGPUProcessEnabled() |
| || !preferences().useGPUProcessForCanvasRenderingEnabled() |
| || !preferences().useGPUProcessForWebGLEnabled()) { |
| parameters.gpuIOKitExtensionHandles = SandboxExtension::createHandlesForIOKitClassExtensions(gpuIOKitClasses(), std::nullopt); |
| parameters.gpuMachExtensionHandles = SandboxExtension::createHandlesForMachLookup(gpuMachServices(), std::nullopt); |
| } |
| #endif |
| #if HAVE(STATIC_FONT_REGISTRY) |
| if (preferences().shouldAllowUserInstalledFonts()) |
| parameters.fontMachExtensionHandle = process.fontdMachExtensionHandle(SandboxExtension::MachBootstrapOptions::DoNotEnableMachBootstrap); |
| #endif |
| #if HAVE(APP_ACCENT_COLORS) |
| parameters.accentColor = pageClient().accentColor(); |
| #endif |
| parameters.shouldScaleViewToFitDocument = m_shouldScaleViewToFitDocument; |
| parameters.userInterfaceLayoutDirection = pageClient().userInterfaceLayoutDirection(); |
| parameters.observedLayoutMilestones = m_observedLayoutMilestones; |
| parameters.overrideContentSecurityPolicy = m_overrideContentSecurityPolicy; |
| parameters.contentSecurityPolicyModeForExtension = m_configuration->contentSecurityPolicyModeForExtension(); |
| parameters.cpuLimit = m_cpuLimit; |
| |
| #if USE(WPE_RENDERER) |
| parameters.hostFileDescriptor = pageClient().hostFileDescriptor(); |
| #endif |
| |
| #if PLATFORM(WIN) |
| parameters.nativeWindowHandle = reinterpret_cast<uint64_t>(viewWidget()); |
| #endif |
| #if USE(GRAPHICS_LAYER_WC) |
| parameters.usesOffscreenRendering = pageClient().usesOffscreenRendering(); |
| #endif |
| |
| for (auto& iterator : m_urlSchemeHandlersByScheme) |
| parameters.urlSchemeHandlers.set(iterator.key, iterator.value->identifier()); |
| parameters.urlSchemesWithLegacyCustomProtocolHandlers = WebProcessPool::urlSchemesWithCustomProtocolHandlers(); |
| |
| #if ENABLE(WEB_RTC) |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.iceCandidateFilteringEnabled = m_preferences->iceCandidateFilteringEnabled(); |
| #if USE(LIBWEBRTC) |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.enumeratingAllNetworkInterfacesEnabled = m_preferences->enumeratingAllNetworkInterfacesEnabled(); |
| #endif |
| #endif |
| |
| #if ENABLE(APPLICATION_MANIFEST) |
| parameters.applicationManifest = m_configuration->applicationManifest() ? std::optional<WebCore::ApplicationManifest>(m_configuration->applicationManifest()->applicationManifest()) : std::nullopt; |
| #endif |
| |
| parameters.needsFontAttributes = m_needsFontAttributes; |
| parameters.backgroundColor = m_backgroundColor; |
| |
| parameters.overriddenMediaType = m_overriddenMediaType; |
| parameters.corsDisablingPatterns = corsDisablingPatterns(); |
| parameters.maskedURLSchemes = m_configuration->maskedURLSchemes(); |
| parameters.userScriptsShouldWaitUntilNotification = m_configuration->userScriptsShouldWaitUntilNotification(); |
| parameters.allowedNetworkHosts = m_configuration->allowedNetworkHosts(); |
| parameters.loadsSubresources = m_configuration->loadsSubresources(); |
| parameters.crossOriginAccessControlCheckEnabled = m_configuration->crossOriginAccessControlCheckEnabled(); |
| parameters.hasResourceLoadClient = !!m_resourceLoadClient; |
| |
| std::reference_wrapper<WebUserContentControllerProxy> userContentController(m_userContentController.get()); |
| if (auto* userContentControllerFromWebsitePolicies = websitePolicies ? websitePolicies->userContentController() : nullptr) |
| userContentController = *userContentControllerFromWebsitePolicies; |
| process.addWebUserContentControllerProxy(userContentController); |
| parameters.userContentControllerParameters = userContentController.get().parameters(); |
| |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldCaptureAudioInUIProcess = preferences().captureAudioInUIProcessEnabled(); |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldCaptureAudioInGPUProcess = preferences().captureAudioInGPUProcessEnabled(); |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldCaptureVideoInUIProcess = preferences().captureVideoInUIProcessEnabled(); |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldCaptureVideoInGPUProcess = preferences().captureVideoInGPUProcessEnabled(); |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldRenderCanvasInGPUProcess = preferences().useGPUProcessForCanvasRenderingEnabled(); |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldRenderDOMInGPUProcess = preferences().useGPUProcessForDOMRenderingEnabled(); |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldPlayMediaInGPUProcess = preferences().useGPUProcessForMediaEnabled(); |
| #if ENABLE(WEBGL) |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldRenderWebGLInGPUProcess = preferences().useGPUProcessForWebGLEnabled(); |
| #endif |
| |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldEnableVP9Decoder = preferences().vp9DecoderEnabled(); |
| #if ENABLE(VP9) && PLATFORM(COCOA) |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldEnableVP8Decoder = preferences().vp8DecoderEnabled(); |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.shouldEnableVP9SWDecoder = preferences().vp9DecoderEnabled() && (!WebCore::systemHasBattery() || preferences().vp9SWDecoderEnabledOnBattery()); |
| #endif |
| parameters.shouldCaptureDisplayInUIProcess = m_process->processPool().configuration().shouldCaptureDisplayInUIProcess(); |
| parameters.shouldCaptureDisplayInGPUProcess = preferences().useGPUProcessForDisplayCapture(); |
| #if ENABLE(APP_BOUND_DOMAINS) |
| parameters.limitsNavigationsToAppBoundDomains = m_limitsNavigationsToAppBoundDomains; |
| #endif |
| parameters.lastNavigationWasAppInitiated = m_lastNavigationWasAppInitiated; |
| parameters.shouldRelaxThirdPartyCookieBlocking = m_configuration->shouldRelaxThirdPartyCookieBlocking(); |
| parameters.canUseCredentialStorage = m_canUseCredentialStorage; |
| |
| parameters.httpsUpgradeEnabled = preferences().upgradeKnownHostsToHTTPSEnabled() ? m_configuration->httpsUpgradeEnabled() : false; |
| |
| #if PLATFORM(IOS) |
| // FIXME: This is also being passed over the to WebProcess via the PreferencesStore. |
| parameters.allowsDeprecatedSynchronousXMLHttpRequestDuringUnload = allowsDeprecatedSynchronousXMLHttpRequestDuringUnload(); |
| #endif |
| |
| #if ENABLE(APP_HIGHLIGHTS) |
| parameters.appHighlightsVisible = appHighlightsVisibility() ? HighlightVisibility::Visible : HighlightVisibility::Hidden; |
| #endif |
| |
| #if HAVE(TOUCH_BAR) |
| parameters.requiresUserActionForEditingControlsManager = m_configuration->requiresUserActionForEditingControlsManager(); |
| #endif |
| |
| #if HAVE(UIKIT_RESIZABLE_WINDOWS) |
| parameters.hasResizableWindows = pageClient().hasResizableWindows(); |
| #endif |
| |
| return parameters; |
| } |
| |
| void WebPageProxy::isJITEnabled(CompletionHandler<void(bool)>&& completionHandler) |
| { |
| launchInitialProcessIfNecessary(); |
| sendWithAsyncReply(Messages::WebProcess::IsJITEnabled(), WTFMove(completionHandler), 0); |
| } |
| |
| void WebPageProxy::enterAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext) |
| { |
| #if PLATFORM(MAC) |
| ASSERT(m_drawingArea->type() == DrawingAreaType::TiledCoreAnimation); |
| #endif |
| pageClient().enterAcceleratedCompositingMode(layerTreeContext); |
| } |
| |
| void WebPageProxy::didFirstLayerFlush(const LayerTreeContext& layerTreeContext) |
| { |
| #if PLATFORM(MAC) |
| ASSERT(m_drawingArea->type() == DrawingAreaType::TiledCoreAnimation); |
| #endif |
| pageClient().didFirstLayerFlush(layerTreeContext); |
| |
| if (m_lastSuspendedPage) |
| m_lastSuspendedPage->pageDidFirstLayerFlush(); |
| m_suspendedPageKeptToPreventFlashing = nullptr; |
| } |
| |
| void WebPageProxy::exitAcceleratedCompositingMode() |
| { |
| pageClient().exitAcceleratedCompositingMode(); |
| } |
| |
| void WebPageProxy::updateAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext) |
| { |
| pageClient().updateAcceleratedCompositingMode(layerTreeContext); |
| } |
| |
| void WebPageProxy::backForwardClear() |
| { |
| m_backForwardList->clear(); |
| } |
| |
| #if ENABLE(GAMEPAD) |
| |
| void WebPageProxy::gamepadActivity(const Vector<GamepadData>& gamepadDatas, EventMakesGamepadsVisible eventVisibility) |
| { |
| send(Messages::WebPage::GamepadActivity(gamepadDatas, eventVisibility)); |
| } |
| |
| #endif |
| |
| void WebPageProxy::didReceiveAuthenticationChallengeProxy(Ref<AuthenticationChallengeProxy>&& authenticationChallenge, NegotiatedLegacyTLS negotiatedLegacyTLS) |
| { |
| if (negotiatedLegacyTLS == NegotiatedLegacyTLS::Yes) { |
| m_navigationClient->shouldAllowLegacyTLS(*this, authenticationChallenge.get(), [this, protectedThis = Ref { *this }, authenticationChallenge] (bool shouldAllowLegacyTLS) { |
| if (shouldAllowLegacyTLS) |
| m_navigationClient->didReceiveAuthenticationChallenge(*this, authenticationChallenge.get()); |
| else |
| authenticationChallenge->listener().completeChallenge(AuthenticationChallengeDisposition::Cancel); |
| }); |
| return; |
| } |
| m_navigationClient->didReceiveAuthenticationChallenge(*this, authenticationChallenge.get()); |
| } |
| |
| void WebPageProxy::negotiatedLegacyTLS() |
| { |
| auto transaction = m_pageLoadState.transaction(); |
| m_pageLoadState.negotiatedLegacyTLS(transaction); |
| } |
| |
| void WebPageProxy::didNegotiateModernTLS(const URL& url) |
| { |
| m_navigationClient->didNegotiateModernTLS(url); |
| } |
| |
| void WebPageProxy::exceededDatabaseQuota(FrameIdentifier frameID, const String& originIdentifier, const String& databaseName, const String& displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, Messages::WebPageProxy::ExceededDatabaseQuota::DelayedReply&& reply) |
| { |
| requestStorageSpace(frameID, originIdentifier, databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage, [reply = WTFMove(reply)](auto quota) mutable { |
| reply(quota); |
| }); |
| } |
| |
| void WebPageProxy::requestStorageSpace(FrameIdentifier frameID, const String& originIdentifier, const String& databaseName, const String& displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, CompletionHandler<void(uint64_t)>&& completionHandler) |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Storage, "requestStorageSpace for frame %" PRIu64 ", current quota %" PRIu64 " current usage %" PRIu64 " expected usage %" PRIu64, frameID.toUInt64(), currentQuota, currentDatabaseUsage, expectedUsage); |
| |
| StorageRequests::singleton().processOrAppend([this, protectedThis = Ref { *this }, pageURL = currentURL(), frameID, originIdentifier, databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage, completionHandler = WTFMove(completionHandler)]() mutable { |
| this->makeStorageSpaceRequest(frameID, originIdentifier, databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage, [this, protectedThis = WTFMove(protectedThis), frameID, pageURL = WTFMove(pageURL), completionHandler = WTFMove(completionHandler), currentQuota](auto quota) mutable { |
| |
| WEBPAGEPROXY_RELEASE_LOG(Storage, "requestStorageSpace response for frame %" PRIu64 ", quota %" PRIu64, frameID.toUInt64(), quota); |
| UNUSED_VARIABLE(frameID); |
| |
| if (quota <= currentQuota && this->currentURL() == pageURL) { |
| WEBPAGEPROXY_RELEASE_LOG(Storage, "storage space increase denied"); |
| m_isQuotaIncreaseDenied = true; |
| } |
| completionHandler(quota); |
| StorageRequests::singleton().processNextIfAny(); |
| }); |
| }); |
| } |
| |
| void WebPageProxy::makeStorageSpaceRequest(FrameIdentifier frameID, const String& originIdentifier, const String& databaseName, const String& displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, CompletionHandler<void(uint64_t)>&& completionHandler) |
| { |
| if (m_isQuotaIncreaseDenied) { |
| completionHandler(currentQuota); |
| return; |
| } |
| |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| auto originData = SecurityOriginData::fromDatabaseIdentifier(originIdentifier); |
| if (originData != SecurityOriginData::fromURL(URL { currentURL() })) { |
| completionHandler(currentQuota); |
| return; |
| } |
| |
| auto origin = API::SecurityOrigin::create(originData->securityOrigin()); |
| m_uiClient->exceededDatabaseQuota(this, frame, origin.ptr(), databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::reachedApplicationCacheOriginQuota(const String& originIdentifier, uint64_t currentQuota, uint64_t totalBytesNeeded, Messages::WebPageProxy::ReachedApplicationCacheOriginQuota::DelayedReply&& reply) |
| { |
| auto securityOriginData = SecurityOriginData::fromDatabaseIdentifier(originIdentifier); |
| MESSAGE_CHECK(m_process, securityOriginData); |
| |
| Ref<SecurityOrigin> securityOrigin = securityOriginData->securityOrigin(); |
| m_uiClient->reachedApplicationCacheOriginQuota(this, securityOrigin.get(), currentQuota, totalBytesNeeded, WTFMove(reply)); |
| } |
| |
| void WebPageProxy::requestGeolocationPermissionForFrame(GeolocationIdentifier geolocationID, FrameInfoData&& frameInfo) |
| { |
| MESSAGE_CHECK(m_process, frameInfo.frameID); |
| auto* frame = m_process->webFrame(*frameInfo.frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| auto request = m_geolocationPermissionRequestManager.createRequest(geolocationID); |
| Function<void(bool)> completionHandler = [request = WTFMove(request)](bool allowed) { |
| if (allowed) |
| request->allow(); |
| else |
| request->deny(); |
| }; |
| |
| // FIXME: Once iOS migrates to the new WKUIDelegate SPI, clean this up |
| // and make it one UIClient call that calls the completionHandler with false |
| // if there is no delegate instead of returning the completionHandler |
| // for other code paths to try. |
| m_uiClient->decidePolicyForGeolocationPermissionRequest(*this, *frame, frameInfo, completionHandler); |
| #if PLATFORM(IOS_FAMILY) |
| if (completionHandler) |
| pageClient().decidePolicyForGeolocationPermissionRequest(*frame, frameInfo, completionHandler); |
| #endif |
| if (completionHandler) |
| completionHandler(false); |
| } |
| |
| void WebPageProxy::revokeGeolocationAuthorizationToken(const String& authorizationToken) |
| { |
| m_geolocationPermissionRequestManager.revokeAuthorizationToken(authorizationToken); |
| } |
| |
| void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const PermissionDescriptor& descriptor, CompletionHandler<void(std::optional<PermissionState>, bool shouldCache)>&& completionHandler) |
| { |
| bool canAPISucceed = true; |
| bool shouldChangeDeniedToPrompt = true; |
| bool shouldChangePromptToGrant = false; |
| String name; |
| if (descriptor.name == PermissionName::Camera) { |
| #if ENABLE(MEDIA_STREAM) |
| name = "camera"_s; |
| canAPISucceed = userMediaPermissionRequestManager().canVideoCaptureSucceed(); |
| shouldChangeDeniedToPrompt = userMediaPermissionRequestManager().shouldChangeDeniedToPromptForCamera(clientOrigin); |
| shouldChangePromptToGrant = userMediaPermissionRequestManager().shouldChangePromptToGrantForCamera(clientOrigin); |
| #endif |
| } else if (descriptor.name == PermissionName::Microphone) { |
| #if ENABLE(MEDIA_STREAM) |
| name = "microphone"_s; |
| canAPISucceed = userMediaPermissionRequestManager().canAudioCaptureSucceed(); |
| shouldChangeDeniedToPrompt = userMediaPermissionRequestManager().shouldChangeDeniedToPromptForMicrophone(clientOrigin); |
| shouldChangePromptToGrant = userMediaPermissionRequestManager().shouldChangePromptToGrantForMicrophone(clientOrigin); |
| #endif |
| } else if (descriptor.name == PermissionName::Geolocation) { |
| #if ENABLE(GEOLOCATION) |
| name = "geolocation"_s; |
| #endif |
| } else if (descriptor.name == PermissionName::Notifications) { |
| #if ENABLE(NOTIFICATIONS) |
| name = "notifications"_s; |
| |
| // Ensure that the true permission state of the Notifications API is returned if |
| // this topOrigin has requested permission to use the Notifications API previously. |
| if (m_notificationPermissionRequesters.contains(clientOrigin.topOrigin)) |
| shouldChangeDeniedToPrompt = false; |
| #endif |
| } |
| |
| if (name.isNull()) { |
| completionHandler({ }, false); |
| return; |
| } |
| |
| if (!canAPISucceed) { |
| completionHandler(shouldChangeDeniedToPrompt ? PermissionState::Prompt : PermissionState::Denied, false); |
| return; |
| } |
| |
| auto origin = API::SecurityOrigin::create(clientOrigin.topOrigin); |
| m_uiClient->queryPermission(name, origin, [clientOrigin, shouldChangeDeniedToPrompt, shouldChangePromptToGrant, completionHandler = WTFMove(completionHandler)](auto result) mutable { |
| if (!result) { |
| completionHandler({ }, false); |
| return; |
| } |
| if (*result == PermissionState::Denied && shouldChangeDeniedToPrompt) |
| result = PermissionState::Prompt; |
| else if (*result == PermissionState::Prompt && shouldChangePromptToGrant) |
| result = PermissionState::Granted; |
| completionHandler(*result, false); |
| }); |
| } |
| |
| #if ENABLE(MEDIA_STREAM) |
| UserMediaPermissionRequestManagerProxy& WebPageProxy::userMediaPermissionRequestManager() |
| { |
| if (m_userMediaPermissionRequestManager) |
| return *m_userMediaPermissionRequestManager; |
| |
| m_userMediaPermissionRequestManager = makeUnique<UserMediaPermissionRequestManagerProxy>(*this); |
| |
| return *m_userMediaPermissionRequestManager; |
| } |
| |
| void WebPageProxy::setMockCaptureDevicesEnabledOverride(std::optional<bool> enabled) |
| { |
| userMediaPermissionRequestManager().setMockCaptureDevicesEnabledOverride(enabled); |
| } |
| |
| void WebPageProxy::willStartCapture(const UserMediaPermissionRequestProxy& request, CompletionHandler<void()>&& callback) |
| { |
| #if ENABLE(GPU_PROCESS) |
| if (!preferences().captureVideoInGPUProcessEnabled() && !preferences().captureAudioInGPUProcessEnabled()) |
| return callback(); |
| |
| auto& gpuProcess = process().processPool().ensureGPUProcess(); |
| gpuProcess.updateCaptureAccess(request.requiresAudioCapture(), request.requiresVideoCapture(), request.requiresDisplayCapture(), m_process->coreProcessIdentifier(), WTFMove(callback)); |
| gpuProcess.updateCaptureOrigin(request.topLevelDocumentSecurityOrigin().data(), m_process->coreProcessIdentifier()); |
| #if PLATFORM(IOS_FAMILY) |
| gpuProcess.setOrientationForMediaCapture(m_deviceOrientation); |
| #endif |
| #else |
| callback(); |
| #endif |
| } |
| |
| #endif |
| |
| void WebPageProxy::requestUserMediaPermissionForFrame(UserMediaRequestIdentifier userMediaID, FrameIdentifier frameID, const WebCore::SecurityOriginData& userMediaDocumentOriginData, const WebCore::SecurityOriginData& topLevelDocumentOriginData, WebCore::MediaStreamRequest&& request) |
| { |
| #if ENABLE(MEDIA_STREAM) |
| MESSAGE_CHECK(m_process, m_process->webFrame(frameID)); |
| #if PLATFORM(MAC) |
| CoreAudioCaptureDeviceManager::singleton().setFilterTapEnabledDevices(!preferences().captureAudioInGPUProcessEnabled()); |
| #endif |
| userMediaPermissionRequestManager().requestUserMediaPermissionForFrame(userMediaID, frameID, userMediaDocumentOriginData.securityOrigin(), topLevelDocumentOriginData.securityOrigin(), WTFMove(request)); |
| #else |
| UNUSED_PARAM(userMediaID); |
| UNUSED_PARAM(frameID); |
| UNUSED_PARAM(userMediaDocumentOriginData); |
| UNUSED_PARAM(topLevelDocumentOriginData); |
| UNUSED_PARAM(request); |
| #endif |
| } |
| |
| void WebPageProxy::enumerateMediaDevicesForFrame(FrameIdentifier frameID, const WebCore::SecurityOriginData& userMediaDocumentOriginData, const WebCore::SecurityOriginData& topLevelDocumentOriginData, CompletionHandler<void(const Vector<CaptureDevice>&, const String&)>&& completionHandler) |
| { |
| #if ENABLE(MEDIA_STREAM) |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| userMediaPermissionRequestManager().enumerateMediaDevicesForFrame(frameID, userMediaDocumentOriginData.securityOrigin(), topLevelDocumentOriginData.securityOrigin(), WTFMove(completionHandler)); |
| #else |
| UNUSED_PARAM(frameID); |
| UNUSED_PARAM(userMediaDocumentOriginData); |
| UNUSED_PARAM(topLevelDocumentOriginData); |
| UNUSED_PARAM(completionHandler); |
| #endif |
| } |
| |
| void WebPageProxy::syncIfMockDevicesEnabledChanged() |
| { |
| #if ENABLE(MEDIA_STREAM) |
| userMediaPermissionRequestManager().syncWithWebCorePrefs(); |
| #endif |
| } |
| |
| void WebPageProxy::beginMonitoringCaptureDevices() |
| { |
| #if ENABLE(MEDIA_STREAM) |
| userMediaPermissionRequestManager().syncWithWebCorePrefs(); |
| UserMediaProcessManager::singleton().beginMonitoringCaptureDevices(); |
| #endif |
| } |
| |
| void WebPageProxy::clearUserMediaState() |
| { |
| #if ENABLE(MEDIA_STREAM) |
| if (m_userMediaPermissionRequestManager) |
| m_userMediaPermissionRequestManager->clearCachedState(); |
| #endif |
| } |
| |
| void WebPageProxy::requestMediaKeySystemPermissionForFrame(MediaKeySystemRequestIdentifier mediaKeySystemID, FrameIdentifier frameID, const WebCore::SecurityOriginData& topLevelDocumentOriginData, const String& keySystem) |
| { |
| #if ENABLE(ENCRYPTED_MEDIA) |
| MESSAGE_CHECK(m_process, m_process->webFrame(frameID)); |
| |
| auto origin = API::SecurityOrigin::create(topLevelDocumentOriginData.securityOrigin()); |
| auto request = mediaKeySystemPermissionRequestManager().createRequestForFrame(mediaKeySystemID, frameID, topLevelDocumentOriginData.securityOrigin(), keySystem); |
| m_uiClient->decidePolicyForMediaKeySystemPermissionRequest(*this, origin, keySystem, [request = WTFMove(request)](bool allowed) { |
| if (allowed) |
| request->allow(); |
| else |
| request->deny(); |
| }); |
| #else |
| UNUSED_PARAM(mediaKeySystemID); |
| UNUSED_PARAM(frameID); |
| UNUSED_PARAM(topLevelDocumentOriginData); |
| UNUSED_PARAM(keySystem); |
| #endif |
| } |
| |
| #if ENABLE(DEVICE_ORIENTATION) |
| void WebPageProxy::shouldAllowDeviceOrientationAndMotionAccess(FrameIdentifier frameID, FrameInfoData&& frameInfo, bool mayPrompt, CompletionHandler<void(DeviceOrientationOrMotionPermissionState)>&& completionHandler) |
| { |
| WebFrameProxy* frame = m_process->webFrame(frameID); |
| MESSAGE_CHECK(m_process, frame); |
| |
| websiteDataStore().deviceOrientationAndMotionAccessController().shouldAllowAccess(*this, *frame, WTFMove(frameInfo), mayPrompt, WTFMove(completionHandler)); |
| } |
| #endif |
| |
| |
| #if ENABLE(IMAGE_ANALYSIS) |
| |
| void WebPageProxy::requestTextRecognition(const URL& imageURL, const ShareableBitmap::Handle& imageData, const String& sourceLanguageIdentifier, const String& targetLanguageIdentifier, CompletionHandler<void(TextRecognitionResult&&)>&& completionHandler) |
| { |
| pageClient().requestTextRecognition(imageURL, imageData, sourceLanguageIdentifier, targetLanguageIdentifier, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::computeHasVisualSearchResults(const URL& imageURL, ShareableBitmap& imageBitmap, CompletionHandler<void(bool)>&& completion) |
| { |
| pageClient().computeHasVisualSearchResults(imageURL, imageBitmap, WTFMove(completion)); |
| } |
| |
| void WebPageProxy::updateWithTextRecognitionResult(TextRecognitionResult&& results, const ElementContext& context, const FloatPoint& location, CompletionHandler<void(TextRecognitionUpdateResult)>&& completionHandler) |
| { |
| if (!hasRunningProcess()) { |
| completionHandler(TextRecognitionUpdateResult::NoText); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::UpdateWithTextRecognitionResult(WTFMove(results), context, location), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::startVisualTranslation(const String& sourceLanguageIdentifier, const String& targetLanguageIdentifier) |
| { |
| if (hasRunningProcess()) |
| send(Messages::WebPage::StartVisualTranslation(sourceLanguageIdentifier, targetLanguageIdentifier)); |
| } |
| |
| #endif // ENABLE(IMAGE_ANALYSIS) |
| |
| void WebPageProxy::requestImageBitmap(const ElementContext& elementContext, CompletionHandler<void(const ShareableBitmap::Handle&, const String&)>&& completion) |
| { |
| if (!hasRunningProcess()) { |
| completion({ }, { }); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::RequestImageBitmap(elementContext), WTFMove(completion)); |
| } |
| |
| #if ENABLE(ENCRYPTED_MEDIA) |
| MediaKeySystemPermissionRequestManagerProxy& WebPageProxy::mediaKeySystemPermissionRequestManager() |
| { |
| if (m_mediaKeySystemPermissionRequestManager) |
| return *m_mediaKeySystemPermissionRequestManager; |
| |
| m_mediaKeySystemPermissionRequestManager = makeUnique<MediaKeySystemPermissionRequestManagerProxy>(*this); |
| return *m_mediaKeySystemPermissionRequestManager; |
| } |
| #endif |
| |
| #if ENABLE(MEDIA_CONTROLS_CONTEXT_MENUS) && USE(UICONTEXTMENU) |
| |
| void WebPageProxy::showMediaControlsContextMenu(FloatRect&& targetFrame, Vector<MediaControlsContextMenuItem>&& items, CompletionHandler<void(MediaControlsContextMenuItem::ID)>&& completionHandler) |
| { |
| pageClient().showMediaControlsContextMenu(WTFMove(targetFrame), WTFMove(items), WTFMove(completionHandler)); |
| } |
| |
| #endif // ENABLE(MEDIA_CONTROLS_CONTEXT_MENUS) && USE(UICONTEXTMENU) |
| |
| #if ENABLE(NOTIFICATIONS) |
| void WebPageProxy::clearNotificationPermissionState() |
| { |
| m_notificationPermissionRequesters.clear(); |
| send(Messages::WebPage::ClearNotificationPermissionState()); |
| } |
| #endif |
| |
| void WebPageProxy::requestNotificationPermission(const String& originString, CompletionHandler<void(bool allowed)>&& completionHandler) |
| { |
| auto origin = API::SecurityOrigin::createFromString(originString); |
| |
| #if ENABLE(NOTIFICATIONS) |
| // Add origin to list of origins that have requested permission to use the Notifications API. |
| m_notificationPermissionRequesters.add(origin->securityOrigin()); |
| #endif |
| |
| m_uiClient->decidePolicyForNotificationPermissionRequest(*this, origin.get(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::showNotification(IPC::Connection& connection, const WebCore::NotificationData& notificationData) |
| { |
| m_process->processPool().supplement<WebNotificationManagerProxy>()->show(this, connection, notificationData); |
| } |
| |
| void WebPageProxy::cancelNotification(const UUID& notificationID) |
| { |
| m_process->processPool().supplement<WebNotificationManagerProxy>()->cancel(this, notificationID); |
| } |
| |
| void WebPageProxy::clearNotifications(const Vector<UUID>& notificationIDs) |
| { |
| m_process->processPool().supplement<WebNotificationManagerProxy>()->clearNotifications(this, notificationIDs); |
| } |
| |
| void WebPageProxy::didDestroyNotification(const UUID& notificationID) |
| { |
| m_process->processPool().supplement<WebNotificationManagerProxy>()->didDestroyNotification(this, notificationID); |
| } |
| |
| float WebPageProxy::headerHeightForPrinting(WebFrameProxy& frame) |
| { |
| if (frame.isDisplayingPDFDocument()) |
| return 0; |
| return m_uiClient->headerHeight(*this, frame); |
| } |
| |
| float WebPageProxy::footerHeightForPrinting(WebFrameProxy& frame) |
| { |
| if (frame.isDisplayingPDFDocument()) |
| return 0; |
| return m_uiClient->footerHeight(*this, frame); |
| } |
| |
| void WebPageProxy::drawHeaderForPrinting(WebFrameProxy& frame, FloatRect&& rect) |
| { |
| if (frame.isDisplayingPDFDocument()) |
| return; |
| m_uiClient->drawHeader(*this, frame, WTFMove(rect)); |
| } |
| |
| void WebPageProxy::drawFooterForPrinting(WebFrameProxy& frame, FloatRect&& rect) |
| { |
| if (frame.isDisplayingPDFDocument()) |
| return; |
| m_uiClient->drawFooter(*this, frame, WTFMove(rect)); |
| } |
| |
| void WebPageProxy::runModal() |
| { |
| // Since runModal() can (and probably will) spin a nested run loop we need to turn off the responsiveness timer. |
| m_process->stopResponsivenessTimer(); |
| |
| // Our Connection's run loop might have more messages waiting to be handled after this RunModal message. |
| // To make sure they are handled inside of the nested modal run loop we must first signal the Connection's |
| // run loop so we're guaranteed that it has a chance to wake up. |
| // See http://webkit.org/b/89590 for more discussion. |
| m_process->connection()->wakeUpRunLoop(); |
| |
| m_uiClient->runModal(*this); |
| } |
| |
| void WebPageProxy::notifyScrollerThumbIsVisibleInRect(const IntRect& scrollerThumb) |
| { |
| m_visibleScrollerThumbRect = scrollerThumb; |
| } |
| |
| void WebPageProxy::recommendedScrollbarStyleDidChange(int32_t newStyle) |
| { |
| #if USE(APPKIT) |
| pageClient().recommendedScrollbarStyleDidChange(static_cast<WebCore::ScrollbarStyle>(newStyle)); |
| #else |
| UNUSED_PARAM(newStyle); |
| #endif |
| } |
| |
| void WebPageProxy::didChangeScrollbarsForMainFrame(bool hasHorizontalScrollbar, bool hasVerticalScrollbar) |
| { |
| m_mainFrameHasHorizontalScrollbar = hasHorizontalScrollbar; |
| m_mainFrameHasVerticalScrollbar = hasVerticalScrollbar; |
| } |
| |
| void WebPageProxy::didChangeScrollOffsetPinningForMainFrame(RectEdges<bool> pinnedState) |
| { |
| pageClient().pinnedStateWillChange(); |
| m_mainFramePinnedState = pinnedState; |
| pageClient().pinnedStateDidChange(); |
| |
| m_uiClient->pinnedStateDidChange(*this); |
| } |
| |
| void WebPageProxy::didChangePageCount(unsigned pageCount) |
| { |
| m_pageCount = pageCount; |
| } |
| |
| void WebPageProxy::themeColorChanged(const Color& themeColor) |
| { |
| if (m_themeColor == themeColor) |
| return; |
| |
| pageClient().themeColorWillChange(); |
| m_themeColor = themeColor; |
| pageClient().themeColorDidChange(); |
| } |
| |
| void WebPageProxy::pageExtendedBackgroundColorDidChange(const Color& newPageExtendedBackgroundColor) |
| { |
| if (m_pageExtendedBackgroundColor == newPageExtendedBackgroundColor) |
| return; |
| |
| auto oldUnderPageBackgroundColor = underPageBackgroundColor(); |
| auto oldPageExtendedBackgroundColor = std::exchange(m_pageExtendedBackgroundColor, newPageExtendedBackgroundColor); |
| bool changesUnderPageBackgroundColor = !equalIgnoringSemanticColor(oldUnderPageBackgroundColor, underPageBackgroundColor()); |
| m_pageExtendedBackgroundColor = WTFMove(oldPageExtendedBackgroundColor); |
| |
| if (changesUnderPageBackgroundColor) |
| pageClient().underPageBackgroundColorWillChange(); |
| pageClient().pageExtendedBackgroundColorWillChange(); |
| |
| m_pageExtendedBackgroundColor = newPageExtendedBackgroundColor; |
| |
| if (changesUnderPageBackgroundColor) |
| pageClient().underPageBackgroundColorDidChange(); |
| pageClient().pageExtendedBackgroundColorDidChange(); |
| } |
| |
| void WebPageProxy::sampledPageTopColorChanged(const Color& sampledPageTopColor) |
| { |
| if (m_sampledPageTopColor == sampledPageTopColor) |
| return; |
| |
| pageClient().sampledPageTopColorWillChange(); |
| m_sampledPageTopColor = sampledPageTopColor; |
| pageClient().sampledPageTopColorDidChange(); |
| } |
| |
| #if !PLATFORM(COCOA) |
| |
| Color WebPageProxy::platformUnderPageBackgroundColor() const |
| { |
| return Color::transparentBlack; |
| } |
| |
| #endif // !PLATFORM(COCOA) |
| |
| bool WebPageProxy::willHandleHorizontalScrollEvents() const |
| { |
| return !m_canShortCircuitHorizontalWheelEvents; |
| } |
| |
| void WebPageProxy::updateWebsitePolicies(WebsitePoliciesData&& websitePolicies) |
| { |
| send(Messages::WebPage::UpdateWebsitePolicies(websitePolicies)); |
| } |
| |
| void WebPageProxy::notifyUserScripts() |
| { |
| m_userScriptsNotified = true; |
| send(Messages::WebPage::NotifyUserScripts()); |
| } |
| |
| bool WebPageProxy::userScriptsNeedNotification() const |
| { |
| if (!m_configuration->userScriptsShouldWaitUntilNotification()) |
| return false; |
| return !m_userScriptsNotified; |
| } |
| |
| void WebPageProxy::didFinishLoadingDataForCustomContentProvider(const String& suggestedFilename, const IPC::DataReference& dataReference) |
| { |
| pageClient().didFinishLoadingDataForCustomContentProvider(ResourceResponseBase::sanitizeSuggestedFilename(suggestedFilename), dataReference); |
| } |
| |
| void WebPageProxy::backForwardRemovedItem(const BackForwardItemIdentifier& itemID) |
| { |
| send(Messages::WebPage::DidRemoveBackForwardItem(itemID)); |
| } |
| |
| void WebPageProxy::setCanRunModal(bool canRunModal) |
| { |
| // It's only possible to change the state for a WebPage which |
| // already qualifies for running modal child web pages, otherwise |
| // there's no other possibility than not allowing it. |
| m_canRunModal = m_uiClient->canRunModal() && canRunModal; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetCanRunModal(m_canRunModal)); |
| } |
| |
| bool WebPageProxy::canRunModal() |
| { |
| return hasRunningProcess() ? m_canRunModal : false; |
| } |
| |
| void WebPageProxy::beginPrinting(WebFrameProxy* frame, const PrintInfo& printInfo) |
| { |
| if (m_isInPrintingMode) |
| return; |
| |
| m_isInPrintingMode = true; |
| send(Messages::WebPage::BeginPrinting(frame->frameID(), printInfo), printingSendOptions(m_isPerformingDOMPrintOperation)); |
| } |
| |
| void WebPageProxy::endPrinting() |
| { |
| if (!m_isInPrintingMode) |
| return; |
| |
| m_isInPrintingMode = false; |
| send(Messages::WebPage::EndPrinting(), printingSendOptions(m_isPerformingDOMPrintOperation)); |
| } |
| |
| uint64_t WebPageProxy::computePagesForPrinting(FrameIdentifier frameID, const PrintInfo& printInfo, CompletionHandler<void(const Vector<WebCore::IntRect>&, double, const WebCore::FloatBoxExtent&)>&& callback) |
| { |
| m_isInPrintingMode = true; |
| return sendWithAsyncReply(Messages::WebPage::ComputePagesForPrinting(frameID, printInfo), WTFMove(callback), printingSendOptions(m_isPerformingDOMPrintOperation)); |
| } |
| |
| #if PLATFORM(COCOA) |
| uint64_t WebPageProxy::drawRectToImage(WebFrameProxy* frame, const PrintInfo& printInfo, const IntRect& rect, const WebCore::IntSize& imageSize, CompletionHandler<void(const WebKit::ShareableBitmap::Handle&)>&& callback) |
| { |
| return sendWithAsyncReply(Messages::WebPage::DrawRectToImage(frame->frameID(), printInfo, rect, imageSize), WTFMove(callback), printingSendOptions(m_isPerformingDOMPrintOperation)); |
| } |
| |
| uint64_t WebPageProxy::drawPagesToPDF(WebFrameProxy* frame, const PrintInfo& printInfo, uint32_t first, uint32_t count, CompletionHandler<void(API::Data*)>&& callback) |
| { |
| return sendWithAsyncReply(Messages::WebPage::DrawPagesToPDF(frame->frameID(), printInfo, first, count), toAPIDataSharedBufferCallback(WTFMove(callback)), printingSendOptions(m_isPerformingDOMPrintOperation)); |
| } |
| #elif PLATFORM(GTK) |
| void WebPageProxy::drawPagesForPrinting(WebFrameProxy* frame, const PrintInfo& printInfo, CompletionHandler<void(API::Error*)>&& callback) |
| { |
| auto callbackWrapper = [callback = WTFMove(callback)] (const WebCore::ResourceError& error) mutable { |
| if (error.isNull()) |
| return callback(nullptr); |
| callback(API::Error::create(error).ptr()); |
| }; |
| m_isInPrintingMode = true; |
| sendWithAsyncReply(Messages::WebPage::DrawPagesForPrinting(frame->frameID(), printInfo), WTFMove(callbackWrapper), printingSendOptions(m_isPerformingDOMPrintOperation)); |
| } |
| #endif |
| |
| #if PLATFORM(COCOA) |
| void WebPageProxy::drawToPDF(FrameIdentifier frameID, const std::optional<FloatRect>& rect, CompletionHandler<void(RefPtr<SharedBuffer>&&)>&& callback) |
| { |
| if (!hasRunningProcess()) { |
| callback({ }); |
| return; |
| } |
| sendWithAsyncReply(Messages::WebPage::DrawToPDF(frameID, rect), WTFMove(callback)); |
| } |
| #endif // PLATFORM(COCOA) |
| |
| void WebPageProxy::getPDFFirstPageSize(WebCore::FrameIdentifier frameID, CompletionHandler<void(WebCore::FloatSize)>&& completionHandler) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetPDFFirstPageSize(frameID), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::updateBackingStoreDiscardableState() |
| { |
| ASSERT(hasRunningProcess()); |
| |
| if (!m_drawingArea) |
| return; |
| |
| bool isDiscardable; |
| |
| if (!m_process->isResponsive()) |
| isDiscardable = false; |
| else |
| isDiscardable = !pageClient().isViewWindowActive() || !isViewVisible(); |
| |
| m_drawingArea->setBackingStoreIsDiscardable(isDiscardable); |
| } |
| |
| void WebPageProxy::saveDataToFileInDownloadsFolder(String&& suggestedFilename, String&& mimeType, URL&& originatingURLString, API::Data& data) |
| { |
| m_uiClient->saveDataToFileInDownloadsFolder(this, ResourceResponseBase::sanitizeSuggestedFilename(suggestedFilename), mimeType, originatingURLString, data); |
| } |
| |
| void WebPageProxy::savePDFToFileInDownloadsFolder(String&& suggestedFilename, URL&& originatingURL, const IPC::DataReference& dataReference) |
| { |
| String sanitizedFilename = ResourceResponseBase::sanitizeSuggestedFilename(suggestedFilename); |
| if (!sanitizedFilename.endsWithIgnoringASCIICase(".pdf"_s)) |
| return; |
| |
| saveDataToFileInDownloadsFolder(WTFMove(sanitizedFilename), "application/pdf"_s, WTFMove(originatingURL), |
| API::Data::create(dataReference.data(), dataReference.size()).get()); |
| } |
| |
| void WebPageProxy::setMinimumSizeForAutoLayout(const IntSize& size) |
| { |
| if (m_minimumSizeForAutoLayout == size) |
| return; |
| |
| m_minimumSizeForAutoLayout = size; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetMinimumSizeForAutoLayout(size)); |
| m_drawingArea->minimumSizeForAutoLayoutDidChange(); |
| |
| #if USE(APPKIT) |
| if (m_minimumSizeForAutoLayout.width() <= 0) |
| didChangeIntrinsicContentSize(IntSize(-1, -1)); |
| #endif |
| } |
| |
| void WebPageProxy::setSizeToContentAutoSizeMaximumSize(const IntSize& size) |
| { |
| if (m_sizeToContentAutoSizeMaximumSize == size) |
| return; |
| |
| m_sizeToContentAutoSizeMaximumSize = size; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetSizeToContentAutoSizeMaximumSize(size)); |
| m_drawingArea->sizeToContentAutoSizeMaximumSizeDidChange(); |
| |
| #if USE(APPKIT) |
| if (m_sizeToContentAutoSizeMaximumSize.width() <= 0) |
| didChangeIntrinsicContentSize(IntSize(-1, -1)); |
| #endif |
| } |
| |
| void WebPageProxy::setAutoSizingShouldExpandToViewHeight(bool shouldExpand) |
| { |
| if (m_autoSizingShouldExpandToViewHeight == shouldExpand) |
| return; |
| |
| m_autoSizingShouldExpandToViewHeight = shouldExpand; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetAutoSizingShouldExpandToViewHeight(shouldExpand)); |
| } |
| |
| void WebPageProxy::setViewportSizeForCSSViewportUnits(const FloatSize& viewportSize) |
| { |
| if (m_viewportSizeForCSSViewportUnits && *m_viewportSizeForCSSViewportUnits == viewportSize) |
| return; |
| |
| m_viewportSizeForCSSViewportUnits = viewportSize; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetViewportSizeForCSSViewportUnits(viewportSize)); |
| } |
| |
| #if USE(AUTOMATIC_TEXT_REPLACEMENT) |
| |
| void WebPageProxy::toggleSmartInsertDelete() |
| { |
| if (TextChecker::isTestingMode()) |
| TextChecker::setSmartInsertDeleteEnabled(!TextChecker::isSmartInsertDeleteEnabled()); |
| } |
| |
| void WebPageProxy::toggleAutomaticQuoteSubstitution() |
| { |
| if (TextChecker::isTestingMode()) |
| TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled); |
| } |
| |
| void WebPageProxy::toggleAutomaticLinkDetection() |
| { |
| if (TextChecker::isTestingMode()) |
| TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled); |
| } |
| |
| void WebPageProxy::toggleAutomaticDashSubstitution() |
| { |
| if (TextChecker::isTestingMode()) |
| TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled); |
| } |
| |
| void WebPageProxy::toggleAutomaticTextReplacement() |
| { |
| if (TextChecker::isTestingMode()) |
| TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled); |
| } |
| |
| #endif |
| |
| #if USE(DICTATION_ALTERNATIVES) |
| |
| void WebPageProxy::showDictationAlternativeUI(const WebCore::FloatRect& boundingBoxOfDictatedText, WebCore::DictationContext dictationContext) |
| { |
| pageClient().showDictationAlternativeUI(boundingBoxOfDictatedText, dictationContext); |
| } |
| |
| void WebPageProxy::removeDictationAlternatives(WebCore::DictationContext dictationContext) |
| { |
| pageClient().removeDictationAlternatives(dictationContext); |
| } |
| |
| void WebPageProxy::dictationAlternatives(WebCore::DictationContext dictationContext, CompletionHandler<void(Vector<String>&&)>&& completionHandler) |
| { |
| completionHandler(pageClient().dictationAlternatives(dictationContext)); |
| } |
| |
| #endif |
| |
| #if PLATFORM(MAC) |
| |
| void WebPageProxy::substitutionsPanelIsShowing(CompletionHandler<void(bool)>&& completionHandler) |
| { |
| completionHandler(TextChecker::substitutionsPanelIsShowing()); |
| } |
| |
| void WebPageProxy::showCorrectionPanel(int32_t panelType, const FloatRect& boundingBoxOfReplacedString, const String& replacedString, const String& replacementString, const Vector<String>& alternativeReplacementStrings) |
| { |
| // FIXME: Make AlternativeTextType an enum class with EnumTraits and serialize it instead of casting to/from an int32_t. |
| pageClient().showCorrectionPanel((AlternativeTextType)panelType, boundingBoxOfReplacedString, replacedString, replacementString, alternativeReplacementStrings); |
| } |
| |
| void WebPageProxy::dismissCorrectionPanel(int32_t reason) |
| { |
| // FIXME: Make ReasonForDismissingAlternativeText an enum class with EnumTraits and serialize it instead of casting to/from an int32_t. |
| pageClient().dismissCorrectionPanel((ReasonForDismissingAlternativeText)reason); |
| } |
| |
| void WebPageProxy::dismissCorrectionPanelSoon(int32_t reason, CompletionHandler<void(String)>&& completionHandler) |
| { |
| // FIXME: Make ReasonForDismissingAlternativeText an enum class with EnumTraits and serialize it instead of casting to/from an int32_t. |
| completionHandler(pageClient().dismissCorrectionPanelSoon((ReasonForDismissingAlternativeText)reason)); |
| } |
| |
| void WebPageProxy::recordAutocorrectionResponse(int32_t response, const String& replacedString, const String& replacementString) |
| { |
| // FIXME: Make AutocorrectionResponse an enum class with EnumTraits and serialize it instead of casting to/from an int32_t. |
| pageClient().recordAutocorrectionResponse(static_cast<AutocorrectionResponse>(response), replacedString, replacementString); |
| } |
| |
| void WebPageProxy::handleAlternativeTextUIResult(const String& result) |
| { |
| if (!isClosed()) |
| send(Messages::WebPage::HandleAlternativeTextUIResult(result)); |
| } |
| |
| void WebPageProxy::setEditableElementIsFocused(bool editableElementIsFocused) |
| { |
| pageClient().setEditableElementIsFocused(editableElementIsFocused); |
| } |
| |
| #endif // PLATFORM(MAC) |
| |
| #if PLATFORM(COCOA) || PLATFORM(GTK) |
| RefPtr<ViewSnapshot> WebPageProxy::takeViewSnapshot(std::optional<WebCore::IntRect>&& clipRect) |
| { |
| return pageClient().takeViewSnapshot(WTFMove(clipRect)); |
| } |
| #endif |
| |
| #if PLATFORM(GTK) || PLATFORM(WPE) |
| void WebPageProxy::cancelComposition(const String& compositionString) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| // Remove any pending composition key event. |
| if (m_keyEventQueue.size() > 1) { |
| auto event = m_keyEventQueue.takeFirst(); |
| m_keyEventQueue.removeAllMatching([](const auto& event) { |
| return event.handledByInputMethod(); |
| }); |
| m_keyEventQueue.prepend(WTFMove(event)); |
| } |
| send(Messages::WebPage::CancelComposition(compositionString)); |
| } |
| |
| void WebPageProxy::deleteSurrounding(int64_t offset, unsigned characterCount) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::DeleteSurrounding(offset, characterCount)); |
| } |
| #endif // PLATFORM(GTK) || PLATFORM(WPE) |
| |
| void WebPageProxy::setScrollPinningBehavior(ScrollPinningBehavior pinning) |
| { |
| if (m_scrollPinningBehavior == pinning) |
| return; |
| |
| m_scrollPinningBehavior = pinning; |
| |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetScrollPinningBehavior(pinning)); |
| } |
| |
| void WebPageProxy::setOverlayScrollbarStyle(std::optional<WebCore::ScrollbarOverlayStyle> scrollbarStyle) |
| { |
| if (!m_scrollbarOverlayStyle && !scrollbarStyle) |
| return; |
| |
| if ((m_scrollbarOverlayStyle && scrollbarStyle) && m_scrollbarOverlayStyle.value() == scrollbarStyle.value()) |
| return; |
| |
| m_scrollbarOverlayStyle = scrollbarStyle; |
| |
| std::optional<uint32_t> scrollbarStyleForMessage; |
| if (scrollbarStyle) |
| scrollbarStyleForMessage = static_cast<ScrollbarOverlayStyle>(scrollbarStyle.value()); |
| |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetScrollbarOverlayStyle(scrollbarStyleForMessage), m_webPageID); |
| } |
| |
| #if ENABLE(WEB_CRYPTO) |
| void WebPageProxy::wrapCryptoKey(const Vector<uint8_t>& key, CompletionHandler<void(bool, Vector<uint8_t>&&)>&& completionHandler) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| Vector<uint8_t> masterKey; |
| |
| if (auto keyData = m_navigationClient->webCryptoMasterKey(*this)) |
| masterKey = Vector(keyData->dataReference()); |
| |
| Vector<uint8_t> wrappedKey; |
| bool succeeded = wrapSerializedCryptoKey(masterKey, key, wrappedKey); |
| completionHandler(succeeded, WTFMove(wrappedKey)); |
| } |
| |
| void WebPageProxy::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, CompletionHandler<void(bool, Vector<uint8_t>&&)>&& completionHandler) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| Vector<uint8_t> masterKey; |
| |
| if (auto keyData = m_navigationClient->webCryptoMasterKey(*this)) |
| masterKey = Vector(keyData->dataReference()); |
| |
| Vector<uint8_t> key; |
| bool succeeded = unwrapSerializedCryptoKey(masterKey, wrappedKey, key); |
| completionHandler(succeeded, WTFMove(key)); |
| } |
| #endif |
| |
| void WebPageProxy::signedPublicKeyAndChallengeString(unsigned keySizeIndex, const String& challengeString, const URL& url, CompletionHandler<void(String)>&& completionHandler) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| if (auto apiString = m_navigationClient->signedPublicKeyAndChallengeString(*this, keySizeIndex, API::String::create(challengeString), url)) |
| return completionHandler(apiString->string()); |
| |
| completionHandler({ }); |
| } |
| |
| void WebPageProxy::addMIMETypeWithCustomContentProvider(const String& mimeType) |
| { |
| send(Messages::WebPage::AddMIMETypeWithCustomContentProvider(mimeType)); |
| } |
| |
| void WebPageProxy::changeFontAttributes(WebCore::FontAttributeChanges&& changes) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ChangeFontAttributes(WTFMove(changes))); |
| } |
| |
| void WebPageProxy::changeFont(WebCore::FontChanges&& changes) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ChangeFont(WTFMove(changes))); |
| } |
| |
| // FIXME: Move these functions to WebPageProxyCocoa.mm. |
| #if PLATFORM(COCOA) |
| |
| void WebPageProxy::setTextAsync(const String& text) |
| { |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetTextAsync(text)); |
| } |
| |
| void WebPageProxy::insertTextAsync(const String& text, const EditingRange& replacementRange, InsertTextOptions&& options) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::InsertTextAsync(text, replacementRange, WTFMove(options))); |
| } |
| |
| void WebPageProxy::hasMarkedText(CompletionHandler<void(bool)>&& callback) |
| { |
| if (!hasRunningProcess()) { |
| callback(false); |
| return; |
| } |
| sendWithAsyncReply(Messages::WebPage::HasMarkedText(), WTFMove(callback)); |
| } |
| |
| void WebPageProxy::getMarkedRangeAsync(CompletionHandler<void(const EditingRange&)>&& callbackFunction) |
| { |
| if (!hasRunningProcess()) { |
| callbackFunction(EditingRange()); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::GetMarkedRangeAsync(), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::getSelectedRangeAsync(CompletionHandler<void(const EditingRange&)>&& callbackFunction) |
| { |
| if (!hasRunningProcess()) { |
| callbackFunction(EditingRange()); |
| return; |
| } |
| |
| sendWithAsyncReply(Messages::WebPage::GetSelectedRangeAsync(), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::characterIndexForPointAsync(const WebCore::IntPoint& point, CompletionHandler<void(uint64_t)>&& callbackFunction) |
| { |
| sendWithAsyncReply(Messages::WebPage::CharacterIndexForPointAsync(point), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::firstRectForCharacterRangeAsync(const EditingRange& range, CompletionHandler<void(const WebCore::IntRect&, const EditingRange&)>&& callbackFunction) |
| { |
| if (!hasRunningProcess()) |
| return callbackFunction({ }, { }); |
| |
| sendWithAsyncReply(Messages::WebPage::FirstRectForCharacterRangeAsync(range), WTFMove(callbackFunction)); |
| } |
| |
| void WebPageProxy::setCompositionAsync(const String& text, const Vector<CompositionUnderline>& underlines, const Vector<CompositionHighlight>& highlights, const EditingRange& selectionRange, const EditingRange& replacementRange) |
| { |
| if (!hasRunningProcess()) { |
| // If this fails, we should call -discardMarkedText on input context to notify the input method. |
| // This will happen naturally later, as part of reloading the page. |
| return; |
| } |
| |
| send(Messages::WebPage::SetCompositionAsync(text, underlines, highlights, selectionRange, replacementRange)); |
| } |
| |
| void WebPageProxy::confirmCompositionAsync() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ConfirmCompositionAsync()); |
| } |
| |
| void WebPageProxy::setScrollPerformanceDataCollectionEnabled(bool enabled) |
| { |
| if (enabled == m_scrollPerformanceDataCollectionEnabled) |
| return; |
| |
| m_scrollPerformanceDataCollectionEnabled = enabled; |
| |
| if (m_scrollPerformanceDataCollectionEnabled && !m_scrollingPerformanceData) |
| m_scrollingPerformanceData = makeUnique<RemoteLayerTreeScrollingPerformanceData>(downcast<RemoteLayerTreeDrawingAreaProxy>(*m_drawingArea)); |
| else if (!m_scrollPerformanceDataCollectionEnabled) |
| m_scrollingPerformanceData = nullptr; |
| } |
| #endif |
| |
| void WebPageProxy::takeSnapshot(IntRect rect, IntSize bitmapSize, SnapshotOptions options, CompletionHandler<void(const ShareableBitmap::Handle&)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::TakeSnapshot(rect, bitmapSize, options), WTFMove(callback)); |
| } |
| |
| void WebPageProxy::navigationGestureDidBegin() |
| { |
| PageClientProtector protector(pageClient()); |
| |
| m_isShowingNavigationGestureSnapshot = true; |
| pageClient().navigationGestureDidBegin(); |
| |
| m_navigationClient->didBeginNavigationGesture(*this); |
| } |
| |
| void WebPageProxy::navigationGestureWillEnd(bool willNavigate, WebBackForwardListItem& item) |
| { |
| PageClientProtector protector(pageClient()); |
| if (willNavigate) { |
| m_isLayerTreeFrozenDueToSwipeAnimation = true; |
| send(Messages::WebPage::FreezeLayerTreeDueToSwipeAnimation()); |
| } |
| |
| pageClient().navigationGestureWillEnd(willNavigate, item); |
| |
| m_navigationClient->willEndNavigationGesture(*this, willNavigate, item); |
| } |
| |
| void WebPageProxy::navigationGestureDidEnd(bool willNavigate, WebBackForwardListItem& item) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| pageClient().navigationGestureDidEnd(willNavigate, item); |
| |
| m_navigationClient->didEndNavigationGesture(*this, willNavigate, item); |
| |
| if (m_isLayerTreeFrozenDueToSwipeAnimation) { |
| m_isLayerTreeFrozenDueToSwipeAnimation = false; |
| send(Messages::WebPage::UnfreezeLayerTreeDueToSwipeAnimation()); |
| |
| if (m_provisionalPage) |
| m_provisionalPage->unfreezeLayerTreeDueToSwipeAnimation(); |
| } |
| } |
| |
| void WebPageProxy::navigationGestureDidEnd() |
| { |
| PageClientProtector protector(pageClient()); |
| |
| pageClient().navigationGestureDidEnd(); |
| } |
| |
| void WebPageProxy::willRecordNavigationSnapshot(WebBackForwardListItem& item) |
| { |
| PageClientProtector protector(pageClient()); |
| |
| pageClient().willRecordNavigationSnapshot(item); |
| } |
| |
| void WebPageProxy::navigationGestureSnapshotWasRemoved() |
| { |
| m_isShowingNavigationGestureSnapshot = false; |
| |
| // The ViewGestureController may call this method on a WebPageProxy whose view has been destroyed. In such case, |
| // we need to return early as the pageClient will not be valid below. |
| if (m_isClosed) |
| return; |
| |
| pageClient().didRemoveNavigationGestureSnapshot(); |
| |
| m_navigationClient->didRemoveNavigationGestureSnapshot(*this); |
| } |
| |
| void WebPageProxy::isPlayingMediaDidChange(MediaProducerMediaStateFlags newState) |
| { |
| #if PLATFORM(IOS_FAMILY) |
| if (!m_process->throttler().shouldBeRunnable()) |
| return; |
| #endif |
| |
| if (!m_isClosed) |
| updatePlayingMediaDidChange(newState, CanDelayNotification::Yes); |
| } |
| |
| void WebPageProxy::updatePlayingMediaDidChange(MediaProducerMediaStateFlags newState, CanDelayNotification canDelayNotification) |
| { |
| #if ENABLE(MEDIA_STREAM) |
| auto updateMediaCaptureStateImmediatelyIfNeeded = [&] { |
| if (canDelayNotification == CanDelayNotification::No && m_updateReportedMediaCaptureStateTimer.isActive()) { |
| m_updateReportedMediaCaptureStateTimer.stop(); |
| updateReportedMediaCaptureState(); |
| } |
| }; |
| #endif |
| |
| if (newState == m_mediaState) { |
| #if ENABLE(MEDIA_STREAM) |
| updateMediaCaptureStateImmediatelyIfNeeded(); |
| #endif |
| return; |
| } |
| |
| #if PLATFORM(MACCATALYST) |
| // When the page starts playing media for the first time, make sure we register with |
| // the EndowmentStateTracker to get notifications when the application is no longer |
| // user-facing, so that we can appropriately suspend all media playback. |
| if (!m_isListeningForUserFacingStateChangeNotification) { |
| EndowmentStateTracker::singleton().addClient(*this); |
| m_isListeningForUserFacingStateChangeNotification = true; |
| } |
| #endif |
| |
| #if ENABLE(MEDIA_STREAM) |
| WebCore::MediaProducerMediaStateFlags oldMediaCaptureState = m_mediaState & WebCore::MediaProducer::MediaCaptureMask; |
| WebCore::MediaProducerMediaStateFlags newMediaCaptureState = newState & WebCore::MediaProducer::MediaCaptureMask; |
| #endif |
| |
| MediaProducerMediaStateFlags playingMediaMask { MediaProducerMediaState::IsPlayingAudio, MediaProducerMediaState::IsPlayingVideo }; |
| MediaProducerMediaStateFlags oldState = m_mediaState; |
| |
| bool playingAudioChanges = (oldState.contains(MediaProducerMediaState::IsPlayingAudio)) != (newState.contains(MediaProducerMediaState::IsPlayingAudio)); |
| if (playingAudioChanges) |
| pageClient().isPlayingAudioWillChange(); |
| m_mediaState = newState; |
| if (playingAudioChanges) |
| pageClient().isPlayingAudioDidChange(); |
| |
| #if ENABLE(MEDIA_STREAM) |
| if (oldMediaCaptureState != newMediaCaptureState) { |
| updateReportedMediaCaptureState(); |
| |
| ASSERT(m_userMediaPermissionRequestManager); |
| if (m_userMediaPermissionRequestManager) |
| m_userMediaPermissionRequestManager->captureStateChanged(oldMediaCaptureState, newMediaCaptureState); |
| } |
| updateMediaCaptureStateImmediatelyIfNeeded(); |
| #endif |
| |
| activityStateDidChange({ ActivityState::IsAudible, ActivityState::IsCapturingMedia }); |
| |
| playingMediaMask.add(WebCore::MediaProducer::MediaCaptureMask); |
| if ((oldState & playingMediaMask) != (m_mediaState & playingMediaMask)) |
| m_uiClient->isPlayingMediaDidChange(*this); |
| |
| if ((oldState.containsAny(MediaProducerMediaState::HasAudioOrVideo)) != (m_mediaState.containsAny(MediaProducerMediaState::HasAudioOrVideo))) |
| videoControlsManagerDidChange(); |
| |
| m_process->updateAudibleMediaAssertions(); |
| } |
| |
| void WebPageProxy::updateReportedMediaCaptureState() |
| { |
| auto activeCaptureState = m_mediaState & MediaProducer::MediaCaptureMask; |
| if (m_reportedMediaCaptureState == activeCaptureState) |
| return; |
| |
| bool haveReportedCapture = m_reportedMediaCaptureState.containsAny(MediaProducer::MediaCaptureMask); |
| bool willReportCapture = !activeCaptureState.isEmpty(); |
| |
| if (haveReportedCapture && !willReportCapture && m_updateReportedMediaCaptureStateTimer.isActive()) |
| return; |
| |
| if (!haveReportedCapture && willReportCapture) |
| m_updateReportedMediaCaptureStateTimer.startOneShot(m_mediaCaptureReportingDelay); |
| |
| WEBPAGEPROXY_RELEASE_LOG(WebRTC, "updateReportedMediaCaptureState: from %d to %d", m_reportedMediaCaptureState.toRaw(), activeCaptureState.toRaw()); |
| |
| bool microphoneCaptureChanged = (m_reportedMediaCaptureState & MediaProducer::MicrophoneCaptureMask) != (activeCaptureState & MediaProducer::MicrophoneCaptureMask); |
| bool cameraCaptureChanged = (m_reportedMediaCaptureState & MediaProducer::VideoCaptureMask) != (activeCaptureState & MediaProducer::VideoCaptureMask); |
| bool displayCaptureChanged = (m_reportedMediaCaptureState & MediaProducer::DisplayCaptureMask) != (activeCaptureState & MediaProducer::DisplayCaptureMask); |
| bool systemAudioCaptureChanged = (m_reportedMediaCaptureState & MediaProducer::SystemAudioCaptureMask) != (activeCaptureState & MediaProducer::SystemAudioCaptureMask); |
| |
| auto reportedDisplayCaptureSurfaces = m_reportedMediaCaptureState & (MediaProducer::ScreenCaptureMask | MediaProducer::WindowCaptureMask); |
| auto activeDisplayCaptureSurfaces = activeCaptureState & (MediaProducer::ScreenCaptureMask | MediaProducer::WindowCaptureMask); |
| auto displayCaptureSurfacesChanged = reportedDisplayCaptureSurfaces != activeDisplayCaptureSurfaces; |
| |
| if (microphoneCaptureChanged) |
| pageClient().microphoneCaptureWillChange(); |
| if (cameraCaptureChanged) |
| pageClient().cameraCaptureWillChange(); |
| if (displayCaptureChanged) |
| pageClient().displayCaptureWillChange(); |
| if (displayCaptureSurfacesChanged) |
| pageClient().displayCaptureSurfacesWillChange(); |
| if (systemAudioCaptureChanged) |
| pageClient().systemAudioCaptureWillChange(); |
| |
| m_reportedMediaCaptureState = activeCaptureState; |
| m_uiClient->mediaCaptureStateDidChange(m_mediaState); |
| |
| if (microphoneCaptureChanged) |
| pageClient().microphoneCaptureChanged(); |
| if (cameraCaptureChanged) |
| pageClient().cameraCaptureChanged(); |
| if (displayCaptureChanged) |
| pageClient().displayCaptureChanged(); |
| if (displayCaptureSurfacesChanged) |
| pageClient().displayCaptureSurfacesChanged(); |
| if (systemAudioCaptureChanged) |
| pageClient().systemAudioCaptureChanged(); |
| } |
| |
| void WebPageProxy::videoControlsManagerDidChange() |
| { |
| pageClient().videoControlsManagerDidChange(); |
| } |
| |
| bool WebPageProxy::hasActiveVideoForControlsManager() const |
| { |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| return m_playbackSessionManager && m_playbackSessionManager->controlsManagerInterface(); |
| #else |
| return false; |
| #endif |
| } |
| |
| void WebPageProxy::requestControlledElementID() const |
| { |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| if (m_playbackSessionManager) |
| m_playbackSessionManager->requestControlledElementID(); |
| #endif |
| } |
| |
| void WebPageProxy::handleControlledElementIDResponse(const String& identifier) const |
| { |
| #if PLATFORM(MAC) |
| pageClient().handleControlledElementIDResponse(identifier); |
| #endif |
| } |
| |
| bool WebPageProxy::isPlayingVideoInEnhancedFullscreen() const |
| { |
| #if ENABLE(VIDEO_PRESENTATION_MODE) |
| return m_videoFullscreenManager && m_videoFullscreenManager->isPlayingVideoInEnhancedFullscreen(); |
| #else |
| return false; |
| #endif |
| } |
| |
| void WebPageProxy::handleAutoplayEvent(WebCore::AutoplayEvent event, OptionSet<AutoplayEventFlags> flags) |
| { |
| m_uiClient->handleAutoplayEvent(*this, event, flags); |
| } |
| |
| #if PLATFORM(MAC) |
| void WebPageProxy::performImmediateActionHitTestAtLocation(FloatPoint point) |
| { |
| send(Messages::WebPage::PerformImmediateActionHitTestAtLocation(point)); |
| } |
| |
| void WebPageProxy::immediateActionDidUpdate() |
| { |
| send(Messages::WebPage::ImmediateActionDidUpdate()); |
| } |
| |
| void WebPageProxy::immediateActionDidCancel() |
| { |
| send(Messages::WebPage::ImmediateActionDidCancel()); |
| } |
| |
| void WebPageProxy::immediateActionDidComplete() |
| { |
| send(Messages::WebPage::ImmediateActionDidComplete()); |
| } |
| |
| void WebPageProxy::didPerformImmediateActionHitTest(const WebHitTestResultData& result, bool contentPreventsDefault, const UserData& userData) |
| { |
| pageClient().didPerformImmediateActionHitTest(result, contentPreventsDefault, m_process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| NSObject *WebPageProxy::immediateActionAnimationControllerForHitTestResult(RefPtr<API::HitTestResult> hitTestResult, uint64_t type, RefPtr<API::Object> userData) |
| { |
| return pageClient().immediateActionAnimationControllerForHitTestResult(hitTestResult, type, userData); |
| } |
| |
| void WebPageProxy::handleAcceptedCandidate(WebCore::TextCheckingResult acceptedCandidate) |
| { |
| send(Messages::WebPage::HandleAcceptedCandidate(acceptedCandidate)); |
| } |
| |
| void WebPageProxy::didHandleAcceptedCandidate() |
| { |
| pageClient().didHandleAcceptedCandidate(); |
| } |
| |
| void WebPageProxy::setUseSystemAppearance(bool useSystemAppearance) |
| { |
| if (useSystemAppearance == m_useSystemAppearance) |
| return; |
| |
| m_useSystemAppearance = useSystemAppearance; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetUseSystemAppearance(useSystemAppearance)); |
| } |
| |
| void WebPageProxy::setHeaderBannerHeightForTesting(int height) |
| { |
| send(Messages::WebPage::SetHeaderBannerHeightForTesting(height)); |
| } |
| |
| void WebPageProxy::setFooterBannerHeightForTesting(int height) |
| { |
| send(Messages::WebPage::SetFooterBannerHeightForTesting(height)); |
| } |
| |
| void WebPageProxy::didEndMagnificationGesture() |
| { |
| send(Messages::WebPage::DidEndMagnificationGesture()); |
| } |
| |
| #endif |
| |
| void WebPageProxy::installActivityStateChangeCompletionHandler(CompletionHandler<void()>&& completionHandler) |
| { |
| if (!hasRunningProcess()) { |
| completionHandler(); |
| return; |
| } |
| |
| m_nextActivityStateChangeCallbacks.append(WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::imageOrMediaDocumentSizeChanged(const WebCore::IntSize& newSize) |
| { |
| m_uiClient->imageOrMediaDocumentSizeChanged(newSize); |
| } |
| |
| void WebPageProxy::setShouldDispatchFakeMouseMoveEvents(bool shouldDispatchFakeMouseMoveEvents) |
| { |
| send(Messages::WebPage::SetShouldDispatchFakeMouseMoveEvents(shouldDispatchFakeMouseMoveEvents)); |
| } |
| |
| void WebPageProxy::handleAutoFillButtonClick(const UserData& userData) |
| { |
| m_uiClient->didClickAutoFillButton(*this, m_process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| void WebPageProxy::didResignInputElementStrongPasswordAppearance(const UserData& userData) |
| { |
| m_uiClient->didResignInputElementStrongPasswordAppearance(*this, m_process->transformHandlesToObjects(userData.object()).get()); |
| } |
| |
| #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS_FAMILY) |
| void WebPageProxy::addPlaybackTargetPickerClient(PlaybackTargetClientContextIdentifier contextId) |
| { |
| pageClient().mediaSessionManager().addPlaybackTargetPickerClient(*this, contextId); |
| } |
| |
| void WebPageProxy::removePlaybackTargetPickerClient(PlaybackTargetClientContextIdentifier contextId) |
| { |
| pageClient().mediaSessionManager().removePlaybackTargetPickerClient(*this, contextId); |
| } |
| |
| void WebPageProxy::showPlaybackTargetPicker(PlaybackTargetClientContextIdentifier contextId, const WebCore::FloatRect& rect, bool hasVideo) |
| { |
| pageClient().mediaSessionManager().showPlaybackTargetPicker(*this, contextId, pageClient().rootViewToScreen(IntRect(rect)), hasVideo, useDarkAppearance()); |
| } |
| |
| void WebPageProxy::playbackTargetPickerClientStateDidChange(PlaybackTargetClientContextIdentifier contextId, WebCore::MediaProducerMediaStateFlags state) |
| { |
| pageClient().mediaSessionManager().clientStateDidChange(*this, contextId, state); |
| } |
| |
| void WebPageProxy::setMockMediaPlaybackTargetPickerEnabled(bool enabled) |
| { |
| pageClient().mediaSessionManager().setMockMediaPlaybackTargetPickerEnabled(enabled); |
| } |
| |
| void WebPageProxy::setMockMediaPlaybackTargetPickerState(const String& name, WebCore::MediaPlaybackTargetContext::MockState state) |
| { |
| pageClient().mediaSessionManager().setMockMediaPlaybackTargetPickerState(name, state); |
| } |
| |
| void WebPageProxy::mockMediaPlaybackTargetPickerDismissPopup() |
| { |
| pageClient().mediaSessionManager().mockMediaPlaybackTargetPickerDismissPopup(); |
| } |
| |
| void WebPageProxy::setPlaybackTarget(PlaybackTargetClientContextIdentifier contextId, Ref<MediaPlaybackTarget>&& target) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| auto context = target->targetContext(); |
| ASSERT(context.type() != MediaPlaybackTargetContext::Type::SerializedAVOutputContext); |
| if (preferences().useGPUProcessForMediaEnabled()) |
| context.serializeOutputContext(); |
| |
| send(Messages::WebPage::PlaybackTargetSelected(contextId, context)); |
| } |
| |
| void WebPageProxy::externalOutputDeviceAvailableDidChange(PlaybackTargetClientContextIdentifier contextId, bool available) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::PlaybackTargetAvailabilityDidChange(contextId, available)); |
| } |
| |
| void WebPageProxy::setShouldPlayToPlaybackTarget(PlaybackTargetClientContextIdentifier contextId, bool shouldPlay) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetShouldPlayToPlaybackTarget(contextId, shouldPlay)); |
| } |
| |
| void WebPageProxy::playbackTargetPickerWasDismissed(PlaybackTargetClientContextIdentifier contextId) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::PlaybackTargetPickerWasDismissed(contextId)); |
| } |
| #endif |
| |
| void WebPageProxy::didExceedInactiveMemoryLimitWhileActive() |
| { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(PerformanceLogging, "didExceedInactiveMemoryLimitWhileActive"); |
| m_uiClient->didExceedBackgroundResourceLimitWhileInForeground(*this, kWKResourceLimitMemory); |
| } |
| |
| void WebPageProxy::didExceedBackgroundCPULimitWhileInForeground() |
| { |
| WEBPAGEPROXY_RELEASE_LOG_ERROR(PerformanceLogging, "didExceedBackgroundCPULimitWhileInForeground"); |
| m_uiClient->didExceedBackgroundResourceLimitWhileInForeground(*this, kWKResourceLimitCPU); |
| } |
| |
| void WebPageProxy::didChangeBackgroundColor() |
| { |
| pageClient().didChangeBackgroundColor(); |
| } |
| |
| void WebPageProxy::clearWheelEventTestMonitor() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::ClearWheelEventTestMonitor()); |
| } |
| |
| void WebPageProxy::callAfterNextPresentationUpdate(WTF::Function<void (CallbackBase::Error)>&& callback) |
| { |
| if (!hasRunningProcess() || !m_drawingArea) { |
| callback(CallbackBase::Error::OwnerWasInvalidated); |
| return; |
| } |
| |
| m_drawingArea->dispatchAfterEnsuringDrawing(WTFMove(callback)); |
| } |
| |
| void WebPageProxy::setShouldScaleViewToFitDocument(bool shouldScaleViewToFitDocument) |
| { |
| if (m_shouldScaleViewToFitDocument == shouldScaleViewToFitDocument) |
| return; |
| |
| m_shouldScaleViewToFitDocument = shouldScaleViewToFitDocument; |
| |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetShouldScaleViewToFitDocument(shouldScaleViewToFitDocument)); |
| } |
| |
| void WebPageProxy::didRestoreScrollPosition() |
| { |
| pageClient().didRestoreScrollPosition(); |
| } |
| |
| void WebPageProxy::getLoadDecisionForIcon(const WebCore::LinkIcon& icon, CallbackID loadIdentifier) |
| { |
| m_iconLoadingClient->getLoadDecisionForIcon(icon, [this, protectedThis = Ref { *this }, loadIdentifier] (CompletionHandler<void(API::Data*)>&& callback) { |
| if (!hasRunningProcess()) { |
| if (callback) |
| callback(nullptr); |
| return; |
| } |
| |
| if (!callback) { |
| sendWithAsyncReply(Messages::WebPage::DidGetLoadDecisionForIcon(false, loadIdentifier), [](auto) { }); |
| return; |
| } |
| sendWithAsyncReply(Messages::WebPage::DidGetLoadDecisionForIcon(true, loadIdentifier), [callback = WTFMove(callback)](const IPC::SharedBufferReference& iconData) mutable { |
| callback(API::Data::create(iconData.data(), iconData.size()).ptr()); |
| }); |
| }); |
| } |
| |
| WebCore::UserInterfaceLayoutDirection WebPageProxy::userInterfaceLayoutDirection() |
| { |
| return pageClient().userInterfaceLayoutDirection(); |
| } |
| |
| void WebPageProxy::setUserInterfaceLayoutDirection(WebCore::UserInterfaceLayoutDirection userInterfaceLayoutDirection) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::SetUserInterfaceLayoutDirection(static_cast<uint32_t>(userInterfaceLayoutDirection))); |
| } |
| |
| void WebPageProxy::hideValidationMessage() |
| { |
| #if PLATFORM(COCOA) || PLATFORM(GTK) |
| m_validationBubble = nullptr; |
| #endif |
| } |
| |
| // FIXME: Consolidate with dismissContentRelativeChildWindows |
| void WebPageProxy::closeOverlayedViews() |
| { |
| hideValidationMessage(); |
| |
| #if ENABLE(DATALIST_ELEMENT) |
| endDataListSuggestions(); |
| #endif |
| |
| #if ENABLE(INPUT_TYPE_COLOR) |
| endColorPicker(); |
| #endif |
| |
| #if ENABLE(DATE_AND_TIME_INPUT_TYPES) |
| endDateTimePicker(); |
| #endif |
| } |
| |
| #if ENABLE(POINTER_LOCK) |
| void WebPageProxy::requestPointerLock() |
| { |
| ASSERT(!m_isPointerLockPending); |
| ASSERT(!m_isPointerLocked); |
| m_isPointerLockPending = true; |
| |
| if (!isViewVisible() || !(m_activityState & ActivityState::IsFocused)) { |
| didDenyPointerLock(); |
| return; |
| } |
| m_uiClient->requestPointerLock(this); |
| } |
| |
| void WebPageProxy::didAllowPointerLock() |
| { |
| ASSERT(m_isPointerLockPending && !m_isPointerLocked); |
| m_isPointerLocked = true; |
| m_isPointerLockPending = false; |
| #if PLATFORM(MAC) |
| CGDisplayHideCursor(CGMainDisplayID()); |
| CGAssociateMouseAndMouseCursorPosition(false); |
| #endif |
| send(Messages::WebPage::DidAcquirePointerLock()); |
| } |
| |
| void WebPageProxy::didDenyPointerLock() |
| { |
| ASSERT(m_isPointerLockPending && !m_isPointerLocked); |
| m_isPointerLockPending = false; |
| send(Messages::WebPage::DidNotAcquirePointerLock()); |
| } |
| |
| void WebPageProxy::requestPointerUnlock() |
| { |
| if (m_isPointerLocked) { |
| #if PLATFORM(MAC) |
| CGAssociateMouseAndMouseCursorPosition(true); |
| CGDisplayShowCursor(CGMainDisplayID()); |
| #endif |
| m_uiClient->didLosePointerLock(this); |
| send(Messages::WebPage::DidLosePointerLock()); |
| } |
| |
| if (m_isPointerLockPending) { |
| m_uiClient->didLosePointerLock(this); |
| send(Messages::WebPage::DidNotAcquirePointerLock()); |
| } |
| |
| m_isPointerLocked = false; |
| m_isPointerLockPending = false; |
| } |
| #endif |
| |
| void WebPageProxy::setURLSchemeHandlerForScheme(Ref<WebURLSchemeHandler>&& handler, const String& scheme) |
| { |
| auto canonicalizedScheme = WTF::URLParser::maybeCanonicalizeScheme(scheme); |
| ASSERT(canonicalizedScheme); |
| ASSERT(!WTF::URLParser::isSpecialScheme(canonicalizedScheme.value())); |
| |
| auto schemeResult = m_urlSchemeHandlersByScheme.add(canonicalizedScheme.value(), handler.get()); |
| ASSERT_UNUSED(schemeResult, schemeResult.isNewEntry); |
| |
| auto handlerIdentifier = handler->identifier(); |
| auto handlerIdentifierResult = m_urlSchemeHandlersByIdentifier.add(handlerIdentifier, WTFMove(handler)); |
| ASSERT_UNUSED(handlerIdentifierResult, handlerIdentifierResult.isNewEntry); |
| |
| send(Messages::WebPage::RegisterURLSchemeHandler(handlerIdentifier, canonicalizedScheme.value())); |
| } |
| |
| WebURLSchemeHandler* WebPageProxy::urlSchemeHandlerForScheme(const String& scheme) |
| { |
| return scheme.isNull() ? nullptr : m_urlSchemeHandlersByScheme.get(scheme); |
| } |
| |
| void WebPageProxy::startURLSchemeTask(URLSchemeTaskParameters&& parameters) |
| { |
| startURLSchemeTaskShared(m_process.copyRef(), m_webPageID, WTFMove(parameters)); |
| } |
| |
| void WebPageProxy::startURLSchemeTaskShared(Ref<WebProcessProxy>&& process, PageIdentifier webPageID, URLSchemeTaskParameters&& parameters) |
| { |
| MESSAGE_CHECK(m_process, decltype(m_urlSchemeHandlersByIdentifier)::isValidKey(parameters.handlerIdentifier)); |
| auto iterator = m_urlSchemeHandlersByIdentifier.find(parameters.handlerIdentifier); |
| MESSAGE_CHECK(process, iterator != m_urlSchemeHandlersByIdentifier.end()); |
| |
| iterator->value->startTask(*this, process, webPageID, WTFMove(parameters), nullptr); |
| } |
| |
| void WebPageProxy::stopURLSchemeTask(WebURLSchemeHandlerIdentifier handlerIdentifier, WebCore::ResourceLoaderIdentifier taskIdentifier) |
| { |
| MESSAGE_CHECK(m_process, decltype(m_urlSchemeHandlersByIdentifier)::isValidKey(handlerIdentifier)); |
| auto iterator = m_urlSchemeHandlersByIdentifier.find(handlerIdentifier); |
| MESSAGE_CHECK(m_process, iterator != m_urlSchemeHandlersByIdentifier.end()); |
| |
| iterator->value->stopTask(*this, taskIdentifier); |
| } |
| |
| void WebPageProxy::loadSynchronousURLSchemeTask(URLSchemeTaskParameters&& parameters, Messages::WebPageProxy::LoadSynchronousURLSchemeTask::DelayedReply&& reply) |
| { |
| MESSAGE_CHECK(m_process, decltype(m_urlSchemeHandlersByIdentifier)::isValidKey(parameters.handlerIdentifier)); |
| auto iterator = m_urlSchemeHandlersByIdentifier.find(parameters.handlerIdentifier); |
| MESSAGE_CHECK(m_process, iterator != m_urlSchemeHandlersByIdentifier.end()); |
| |
| iterator->value->startTask(*this, m_process, m_webPageID, WTFMove(parameters), WTFMove(reply)); |
| } |
| |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| void WebPageProxy::requestStorageAccessConfirm(const RegistrableDomain& subFrameDomain, const RegistrableDomain& topFrameDomain, FrameIdentifier frameID, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| m_uiClient->requestStorageAccessConfirm(*this, m_process->webFrame(frameID), subFrameDomain, topFrameDomain, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::didCommitCrossSiteLoadWithDataTransferFromPrevalentResource() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::WasLoadedWithDataTransferFromPrevalentResource()); |
| } |
| #endif |
| |
| bool WebPageProxy::useDarkAppearance() const |
| { |
| return pageClient().effectiveAppearanceIsDark(); |
| } |
| |
| bool WebPageProxy::useElevatedUserInterfaceLevel() const |
| { |
| return pageClient().effectiveUserInterfaceLevelIsElevated(); |
| } |
| |
| void WebPageProxy::effectiveAppearanceDidChange() |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| send(Messages::WebPage::EffectiveAppearanceDidChange(useDarkAppearance(), useElevatedUserInterfaceLevel())); |
| } |
| |
| #if HAVE(TOUCH_BAR) |
| void WebPageProxy::touchBarMenuDataChanged(const TouchBarMenuData& touchBarMenuData) |
| { |
| m_touchBarMenuData = touchBarMenuData; |
| } |
| |
| void WebPageProxy::touchBarMenuItemDataAdded(const TouchBarMenuItemData& touchBarMenuItemData) |
| { |
| m_touchBarMenuData.addMenuItem(touchBarMenuItemData); |
| } |
| |
| void WebPageProxy::touchBarMenuItemDataRemoved(const TouchBarMenuItemData& touchBarMenuItemData) |
| { |
| m_touchBarMenuData.removeMenuItem(touchBarMenuItemData); |
| } |
| #endif |
| |
| #if HAVE(PASTEBOARD_DATA_OWNER) |
| |
| DataOwnerType WebPageProxy::dataOwnerForPasteboard(PasteboardAccessIntent intent) const |
| { |
| return pageClient().dataOwnerForPasteboard(intent); |
| } |
| |
| #endif |
| |
| #if ENABLE(ATTACHMENT_ELEMENT) |
| |
| #if PLATFORM(IOS_FAMILY) |
| void WebPageProxy::writePromisedAttachmentToPasteboard(WebCore::PromisedAttachmentInfo&& info, const String& authorizationToken) |
| { |
| MESSAGE_CHECK(m_process, isValidPerformActionOnElementAuthorizationToken(authorizationToken)); |
| |
| pageClient().writePromisedAttachmentToPasteboard(WTFMove(info)); |
| } |
| #endif |
| |
| void WebPageProxy::requestAttachmentIcon(const String& identifier, const String& contentType, const String& fileName, const String& title, const FloatSize& requestedSize) |
| { |
| MESSAGE_CHECK(m_process, m_preferences->attachmentElementEnabled()); |
| |
| FloatSize size = requestedSize; |
| ShareableBitmap::Handle handle; |
| #if PLATFORM(MAC) |
| if (auto attachment = attachmentForIdentifier(identifier); attachment && attachment->contentType() == "public.directory"_s) { |
| attachment->doWithFileWrapper([&](NSFileWrapper *fileWrapper) { |
| updateIconForDirectory(fileWrapper, attachment->identifier()); |
| }); |
| return; |
| } |
| #endif |
| |
| #if PLATFORM(COCOA) |
| if (auto icon = iconForAttachment(fileName, contentType, title, size)) |
| icon->createHandle(handle); |
| #endif |
| send(Messages::WebPage::UpdateAttachmentIcon(identifier, handle, size)); |
| } |
| |
| RefPtr<API::Attachment> WebPageProxy::attachmentForIdentifier(const String& identifier) const |
| { |
| if (identifier.isEmpty()) |
| return nullptr; |
| |
| return m_attachmentIdentifierToAttachmentMap.get(identifier); |
| } |
| |
| void WebPageProxy::insertAttachment(Ref<API::Attachment>&& attachment, CompletionHandler<void()>&& callback) |
| { |
| auto attachmentIdentifier = attachment->identifier(); |
| sendWithAsyncReply(Messages::WebPage::InsertAttachment(attachmentIdentifier, attachment->fileSizeForDisplay(), attachment->fileName(), attachment->contentType()), WTFMove(callback)); |
| m_attachmentIdentifierToAttachmentMap.set(attachmentIdentifier, WTFMove(attachment)); |
| } |
| |
| void WebPageProxy::updateAttachmentAttributes(const API::Attachment& attachment, CompletionHandler<void()>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::UpdateAttachmentAttributes(attachment.identifier(), attachment.fileSizeForDisplay(), attachment.contentType(), attachment.fileName(), IPC::SharedBufferReference(attachment.enclosingImageData())), WTFMove(callback)); |
| |
| #if HAVE(QUICKLOOK_THUMBNAILING) |
| requestThumbnail(attachment, attachment.identifier()); |
| #endif |
| } |
| |
| #if HAVE(QUICKLOOK_THUMBNAILING) |
| void WebPageProxy::updateAttachmentThumbnail(const String& identifier, const RefPtr<ShareableBitmap>& bitmap) |
| { |
| if (!hasRunningProcess()) |
| return; |
| |
| ShareableBitmap::Handle handle; |
| if (bitmap) |
| bitmap->createHandle(handle); |
| |
| send(Messages::WebPage::UpdateAttachmentThumbnail(identifier, handle)); |
| } |
| #endif |
| |
| void WebPageProxy::registerAttachmentIdentifierFromData(const String& identifier, const String& contentType, const String& preferredFileName, const IPC::SharedBufferReference& data) |
| { |
| MESSAGE_CHECK(m_process, m_preferences->attachmentElementEnabled()); |
| MESSAGE_CHECK(m_process, IdentifierToAttachmentMap::isValidKey(identifier)); |
| |
| if (attachmentForIdentifier(identifier)) |
| return; |
| |
| auto attachment = ensureAttachment(identifier); |
| attachment->setContentType(contentType); |
| m_attachmentIdentifierToAttachmentMap.set(identifier, attachment.copyRef()); |
| |
| platformRegisterAttachment(WTFMove(attachment), preferredFileName, data); |
| } |
| |
| void WebPageProxy::registerAttachmentIdentifierFromFilePath(const String& identifier, const String& contentType, const String& filePath) |
| { |
| MESSAGE_CHECK(m_process, m_preferences->attachmentElementEnabled()); |
| MESSAGE_CHECK(m_process, IdentifierToAttachmentMap::isValidKey(identifier)); |
| |
| if (attachmentForIdentifier(identifier)) |
| return; |
| |
| auto attachment = ensureAttachment(identifier); |
| attachment->setContentType(contentType); |
| attachment->setFilePath(filePath); |
| m_attachmentIdentifierToAttachmentMap.set(identifier, attachment.copyRef()); |
| platformRegisterAttachment(WTFMove(attachment), filePath); |
| #if HAVE(QUICKLOOK_THUMBNAILING) |
| requestThumbnailWithPath(filePath, identifier); |
| #endif |
| } |
| |
| void WebPageProxy::registerAttachmentIdentifier(const String& identifier) |
| { |
| MESSAGE_CHECK(m_process, m_preferences->attachmentElementEnabled()); |
| MESSAGE_CHECK(m_process, IdentifierToAttachmentMap::isValidKey(identifier)); |
| |
| if (!attachmentForIdentifier(identifier)) |
| m_attachmentIdentifierToAttachmentMap.set(identifier, ensureAttachment(identifier)); |
| } |
| |
| void WebPageProxy::registerAttachmentsFromSerializedData(Vector<WebCore::SerializedAttachmentData>&& data) |
| { |
| MESSAGE_CHECK(m_process, m_preferences->attachmentElementEnabled()); |
| |
| for (auto& serializedData : data) { |
| auto identifier = WTFMove(serializedData.identifier); |
| if (!attachmentForIdentifier(identifier)) { |
| auto attachment = ensureAttachment(identifier); |
| attachment->updateFromSerializedRepresentation(WTFMove(serializedData.data), WTFMove(serializedData.mimeType)); |
| #if HAVE(QUICKLOOK_THUMBNAILING) |
| requestThumbnail(attachment, identifier); |
| #endif |
| } |
| } |
| } |
| |
| void WebPageProxy::cloneAttachmentData(const String& fromIdentifier, const String& toIdentifier) |
| { |
| MESSAGE_CHECK(m_process, m_preferences->attachmentElementEnabled()); |
| MESSAGE_CHECK(m_process, IdentifierToAttachmentMap::isValidKey(fromIdentifier)); |
| MESSAGE_CHECK(m_process, IdentifierToAttachmentMap::isValidKey(toIdentifier)); |
| |
| auto newAttachment = ensureAttachment(toIdentifier); |
| auto existingAttachment = attachmentForIdentifier(fromIdentifier); |
| if (!existingAttachment) { |
| ASSERT_NOT_REACHED(); |
| return; |
| } |
| |
| newAttachment->setContentType(existingAttachment->contentType()); |
| newAttachment->setFilePath(existingAttachment->filePath()); |
| |
| platformCloneAttachment(existingAttachment.releaseNonNull(), WTFMove(newAttachment)); |
| } |
| |
| void WebPageProxy::invalidateAllAttachments() |
| { |
| for (auto& attachment : m_attachmentIdentifierToAttachmentMap.values()) { |
| if (attachment->insertionState() == API::Attachment::InsertionState::Inserted) |
| didRemoveAttachment(attachment.get()); |
| attachment->invalidate(); |
| } |
| m_attachmentIdentifierToAttachmentMap.clear(); |
| } |
| |
| void WebPageProxy::serializedAttachmentDataForIdentifiers(const Vector<String>& identifiers, CompletionHandler<void(Vector<WebCore::SerializedAttachmentData>&&)>&& completionHandler) |
| { |
| Vector<WebCore::SerializedAttachmentData> serializedData; |
| |
| MESSAGE_CHECK_COMPLETION(m_process, m_preferences->attachmentElementEnabled(), completionHandler(WTFMove(serializedData))); |
| |
| for (const auto& identifier : identifiers) { |
| auto attachment = attachmentForIdentifier(identifier); |
| if (!attachment) |
| continue; |
| |
| auto data = attachment->createSerializedRepresentation(); |
| if (!data) |
| continue; |
| |
| serializedData.append({ identifier, attachment->mimeType(), data.releaseNonNull() }); |
| } |
| completionHandler(WTFMove(serializedData)); |
| } |
| |
| void WebPageProxy::didInvalidateDataForAttachment(API::Attachment& attachment) |
| { |
| pageClient().didInvalidateDataForAttachment(attachment); |
| } |
| |
| WebPageProxy::ShouldUpdateAttachmentAttributes WebPageProxy::willUpdateAttachmentAttributes(const API::Attachment& attachment) |
| { |
| return ShouldUpdateAttachmentAttributes::Yes; |
| } |
| |
| #if !PLATFORM(COCOA) |
| |
| void WebPageProxy::platformRegisterAttachment(Ref<API::Attachment>&&, const String&, const IPC::SharedBufferReference&) |
| { |
| } |
| |
| void WebPageProxy::platformRegisterAttachment(Ref<API::Attachment>&&, const String&) |
| { |
| } |
| |
| void WebPageProxy::platformCloneAttachment(Ref<API::Attachment>&&, Ref<API::Attachment>&&) |
| { |
| } |
| |
| #endif |
| |
| void WebPageProxy::didInsertAttachmentWithIdentifier(const String& identifier, const String& source, bool hasEnclosingImage) |
| { |
| MESSAGE_CHECK(m_process, m_preferences->attachmentElementEnabled()); |
| MESSAGE_CHECK(m_process, IdentifierToAttachmentMap::isValidKey(identifier)); |
| |
| auto attachment = ensureAttachment(identifier); |
| attachment->setHasEnclosingImage(hasEnclosingImage); |
| attachment->setInsertionState(API::Attachment::InsertionState::Inserted); |
| pageClient().didInsertAttachment(attachment.get(), source); |
| |
| if (!attachment->isEmpty() && hasEnclosingImage) |
| updateAttachmentAttributes(attachment.get(), [] { }); |
| } |
| |
| void WebPageProxy::didRemoveAttachmentWithIdentifier(const String& identifier) |
| { |
| MESSAGE_CHECK(m_process, m_preferences->attachmentElementEnabled()); |
| MESSAGE_CHECK(m_process, IdentifierToAttachmentMap::isValidKey(identifier)); |
| |
| if (auto attachment = attachmentForIdentifier(identifier)) |
| didRemoveAttachment(*attachment); |
| } |
| |
| void WebPageProxy::didRemoveAttachment(API::Attachment& attachment) |
| { |
| attachment.setInsertionState(API::Attachment::InsertionState::NotInserted); |
| pageClient().didRemoveAttachment(attachment); |
| } |
| |
| Ref<API::Attachment> WebPageProxy::ensureAttachment(const String& identifier) |
| { |
| if (auto existingAttachment = attachmentForIdentifier(identifier)) |
| return *existingAttachment; |
| |
| auto attachment = API::Attachment::create(identifier, *this); |
| m_attachmentIdentifierToAttachmentMap.set(identifier, attachment.copyRef()); |
| return attachment; |
| } |
| |
| #endif // ENABLE(ATTACHMENT_ELEMENT) |
| |
| #if ENABLE(APPLICATION_MANIFEST) |
| void WebPageProxy::getApplicationManifest(CompletionHandler<void(const std::optional<WebCore::ApplicationManifest>&)>&& callback) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetApplicationManifest(), WTFMove(callback)); |
| } |
| #endif |
| |
| #if ENABLE(APP_HIGHLIGHTS) |
| void WebPageProxy::storeAppHighlight(const WebCore::AppHighlight& highlight) |
| { |
| MESSAGE_CHECK(m_process, !highlight.highlight->isEmpty()); |
| |
| pageClient().storeAppHighlight(highlight); |
| |
| } |
| #endif |
| |
| namespace { |
| enum class CompletionCondition { |
| Cancellation, |
| Error, |
| Success, |
| Timeout, |
| }; |
| struct MessageType { |
| CompletionCondition condition; |
| Seconds seconds; |
| String message; |
| }; |
| } |
| |
| void WebPageProxy::reportPageLoadResult(const ResourceError& error) |
| { |
| static const NeverDestroyed<Vector<MessageType>> messages(std::initializer_list<MessageType> { |
| { CompletionCondition::Cancellation, 2_s, DiagnosticLoggingKeys::canceledLessThan2SecondsKey() }, |
| { CompletionCondition::Cancellation, 5_s, DiagnosticLoggingKeys::canceledLessThan5SecondsKey() }, |
| { CompletionCondition::Cancellation, 20_s, DiagnosticLoggingKeys::canceledLessThan20SecondsKey() }, |
| { CompletionCondition::Cancellation, Seconds::infinity(), DiagnosticLoggingKeys::canceledMoreThan20SecondsKey() }, |
| |
| { CompletionCondition::Error, 2_s, DiagnosticLoggingKeys::failedLessThan2SecondsKey() }, |
| { CompletionCondition::Error, 5_s, DiagnosticLoggingKeys::failedLessThan5SecondsKey() }, |
| { CompletionCondition::Error, 20_s, DiagnosticLoggingKeys::failedLessThan20SecondsKey() }, |
| { CompletionCondition::Error, Seconds::infinity(), DiagnosticLoggingKeys::failedMoreThan20SecondsKey() }, |
| |
| { CompletionCondition::Success, 2_s, DiagnosticLoggingKeys::succeededLessThan2SecondsKey() }, |
| { CompletionCondition::Success, 5_s, DiagnosticLoggingKeys::succeededLessThan5SecondsKey() }, |
| { CompletionCondition::Success, 20_s, DiagnosticLoggingKeys::succeededLessThan20SecondsKey() }, |
| { CompletionCondition::Success, Seconds::infinity(), DiagnosticLoggingKeys::succeededMoreThan20SecondsKey() }, |
| |
| { CompletionCondition::Timeout, Seconds::infinity(), DiagnosticLoggingKeys::timedOutKey() } |
| }); |
| |
| if (!m_pageLoadStart) |
| return; |
| |
| auto pageLoadTime = MonotonicTime::now() - *m_pageLoadStart; |
| m_pageLoadStart = std::nullopt; |
| |
| CompletionCondition condition { CompletionCondition::Success }; |
| if (error.isCancellation()) |
| condition = CompletionCondition::Cancellation; |
| else if (error.isTimeout()) |
| condition = CompletionCondition::Timeout; |
| else if (!error.isNull() || error.errorCode()) |
| condition = CompletionCondition::Error; |
| |
| for (auto& messageItem : messages.get()) { |
| if (condition == messageItem.condition && pageLoadTime < messageItem.seconds) { |
| logDiagnosticMessage(DiagnosticLoggingKeys::telemetryPageLoadKey(), messageItem.message, ShouldSample::No); |
| logDiagnosticMessage(DiagnosticLoggingKeys::telemetryPageLoadKey(), DiagnosticLoggingKeys::occurredKey(), ShouldSample::No); |
| break; |
| } |
| } |
| } |
| |
| void WebPageProxy::setDefersLoadingForTesting(bool defersLoading) |
| { |
| send(Messages::WebPage::SetDefersLoading(defersLoading)); |
| } |
| |
| void WebPageProxy::getIsViewVisible(bool& result) |
| { |
| result = isViewVisible(); |
| } |
| |
| void WebPageProxy::updateCurrentModifierState() |
| { |
| #if PLATFORM(COCOA) |
| auto modifiers = PlatformKeyboardEvent::currentStateOfModifierKeys(); |
| send(Messages::WebPage::UpdateCurrentModifierState(modifiers)); |
| #endif |
| } |
| |
| bool WebPageProxy::checkURLReceivedFromCurrentOrPreviousWebProcess(WebProcessProxy& process, const String& urlString) |
| { |
| return checkURLReceivedFromCurrentOrPreviousWebProcess(process, URL { urlString }); |
| } |
| |
| bool WebPageProxy::checkURLReceivedFromCurrentOrPreviousWebProcess(WebProcessProxy& process, const URL& url) |
| { |
| if (!url.isLocalFile()) |
| return true; |
| |
| if (m_mayHaveUniversalFileReadSandboxExtension) |
| return true; |
| |
| String path = url.fileSystemPath(); |
| auto startsWithURLPath = [&path](const String& visitedPath) { |
| return path.startsWith(visitedPath); |
| }; |
| |
| auto localPathsEnd = m_previouslyVisitedPaths.end(); |
| if (std::find_if(m_previouslyVisitedPaths.begin(), localPathsEnd, startsWithURLPath) != localPathsEnd) |
| return true; |
| |
| return process.checkURLReceivedFromWebProcess(url); |
| } |
| |
| void WebPageProxy::addPreviouslyVisitedPath(const String& path) |
| { |
| m_previouslyVisitedPaths.add(path); |
| } |
| |
| void WebPageProxy::willAcquireUniversalFileReadSandboxExtension(WebProcessProxy& process) |
| { |
| m_mayHaveUniversalFileReadSandboxExtension = true; |
| process.willAcquireUniversalFileReadSandboxExtension(); |
| } |
| |
| void WebPageProxy::simulateDeviceOrientationChange(double alpha, double beta, double gamma) |
| { |
| send(Messages::WebPage::SimulateDeviceOrientationChange(alpha, beta, gamma)); |
| } |
| |
| #if ENABLE(DATA_DETECTION) |
| |
| void WebPageProxy::detectDataInAllFrames(OptionSet<WebCore::DataDetectorType> types, CompletionHandler<void(const DataDetectionResult&)>&& completionHandler) |
| { |
| if (!hasRunningProcess()) { |
| completionHandler({ }); |
| return; |
| } |
| sendWithAsyncReply(Messages::WebPage::DetectDataInAllFrames(types), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::removeDataDetectedLinks(CompletionHandler<void(const DataDetectionResult&)>&& completionHandler) |
| { |
| if (!hasRunningProcess()) { |
| completionHandler({ }); |
| return; |
| } |
| sendWithAsyncReply(Messages::WebPage::RemoveDataDetectedLinks(), WTFMove(completionHandler)); |
| } |
| |
| #endif |
| |
| #if USE(SYSTEM_PREVIEW) |
| void WebPageProxy::systemPreviewActionTriggered(const WebCore::SystemPreviewInfo& previewInfo, const String& message) |
| { |
| send(Messages::WebPage::SystemPreviewActionTriggered(previewInfo, message)); |
| } |
| #endif |
| |
| void WebPageProxy::dumpPrivateClickMeasurement(CompletionHandler<void(const String&)>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::DumpPrivateClickMeasurement(m_websiteDataStore->sessionID()), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::clearPrivateClickMeasurement(CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::ClearPrivateClickMeasurement(m_websiteDataStore->sessionID()), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setPrivateClickMeasurementOverrideTimerForTesting(bool value, CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::SetPrivateClickMeasurementOverrideTimerForTesting(m_websiteDataStore->sessionID(), value), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::markAttributedPrivateClickMeasurementsAsExpiredForTesting(CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::MarkAttributedPrivateClickMeasurementsAsExpiredForTesting(m_websiteDataStore->sessionID()), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setPrivateClickMeasurementEphemeralMeasurementForTesting(bool value, CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::SetPrivateClickMeasurementEphemeralMeasurementForTesting(m_websiteDataStore->sessionID(), value), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::simulatePrivateClickMeasurementSessionRestart(CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::SimulatePrivateClickMeasurementSessionRestart(m_websiteDataStore->sessionID()), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setPrivateClickMeasurementTokenPublicKeyURLForTesting(const URL& url, CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::SetPrivateClickMeasurementTokenPublicKeyURLForTesting(m_websiteDataStore->sessionID(), url), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setPrivateClickMeasurementTokenSignatureURLForTesting(const URL& url, CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::SetPrivateClickMeasurementTokenSignatureURLForTesting(m_websiteDataStore->sessionID(), url), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setPrivateClickMeasurementAttributionReportURLsForTesting(const URL& sourceURL, const URL& destinationURL, CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::SetPrivateClickMeasurementAttributionReportURLsForTesting(m_websiteDataStore->sessionID(), sourceURL, destinationURL), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::markPrivateClickMeasurementsAsExpiredForTesting(CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::MarkPrivateClickMeasurementsAsExpiredForTesting(m_websiteDataStore->sessionID()), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setPCMFraudPreventionValuesForTesting(const String& unlinkableToken, const String& secretToken, const String& signature, const String& keyID, CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::SetPCMFraudPreventionValuesForTesting(m_websiteDataStore->sessionID(), unlinkableToken, secretToken, signature, keyID), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setPrivateClickMeasurementAppBundleIDForTesting(const String& appBundleIDForTesting, CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::SetPrivateClickMeasurementAppBundleIDForTesting(m_websiteDataStore->sessionID(), appBundleIDForTesting), WTFMove(completionHandler)); |
| } |
| |
| #if ENABLE(SPEECH_SYNTHESIS) |
| |
| void WebPageProxy::resetSpeechSynthesizer() |
| { |
| if (!m_speechSynthesisData) |
| return; |
| |
| auto& synthesisData = speechSynthesisData(); |
| synthesisData.speakingFinishedCompletionHandler = nullptr; |
| synthesisData.speakingStartedCompletionHandler = nullptr; |
| synthesisData.speakingPausedCompletionHandler = nullptr; |
| synthesisData.speakingResumedCompletionHandler = nullptr; |
| if (synthesisData.synthesizer) |
| synthesisData.synthesizer->resetState(); |
| } |
| |
| WebPageProxy::SpeechSynthesisData& WebPageProxy::speechSynthesisData() |
| { |
| if (!m_speechSynthesisData) |
| m_speechSynthesisData = SpeechSynthesisData { makeUnique<PlatformSpeechSynthesizer>(this), nullptr, nullptr, nullptr, nullptr, nullptr }; |
| return *m_speechSynthesisData; |
| } |
| |
| void WebPageProxy::speechSynthesisVoiceList(CompletionHandler<void(Vector<WebSpeechSynthesisVoice>&&)>&& completionHandler) |
| { |
| auto result = speechSynthesisData().synthesizer->voiceList().map([](auto& voice) { |
| return WebSpeechSynthesisVoice { voice->voiceURI(), voice->name(), voice->lang(), voice->localService(), voice->isDefault() }; |
| }); |
| completionHandler(WTFMove(result)); |
| } |
| |
| void WebPageProxy::speechSynthesisSetFinishedCallback(CompletionHandler<void()>&& completionHandler) |
| { |
| speechSynthesisData().speakingFinishedCompletionHandler = WTFMove(completionHandler); |
| } |
| |
| void WebPageProxy::speechSynthesisSpeak(const String& text, const String& lang, float volume, float rate, float pitch, MonotonicTime startTime, const String& voiceURI, const String& voiceName, const String& voiceLang, bool localService, bool defaultVoice, CompletionHandler<void()>&& completionHandler) |
| { |
| auto voice = WebCore::PlatformSpeechSynthesisVoice::create(voiceURI, voiceName, voiceLang, localService, defaultVoice); |
| auto utterance = WebCore::PlatformSpeechSynthesisUtterance::create(*this); |
| utterance->setText(text); |
| utterance->setLang(lang); |
| utterance->setVolume(volume); |
| utterance->setRate(rate); |
| utterance->setPitch(pitch); |
| utterance->setVoice(&voice.get()); |
| |
| speechSynthesisData().speakingStartedCompletionHandler = WTFMove(completionHandler); |
| speechSynthesisData().utterance = WTFMove(utterance); |
| speechSynthesisData().synthesizer->speak(m_speechSynthesisData->utterance.get()); |
| } |
| |
| void WebPageProxy::speechSynthesisCancel() |
| { |
| speechSynthesisData().synthesizer->cancel(); |
| } |
| |
| void WebPageProxy::speechSynthesisResetState() |
| { |
| speechSynthesisData().synthesizer->resetState(); |
| } |
| |
| void WebPageProxy::speechSynthesisPause(CompletionHandler<void()>&& completionHandler) |
| { |
| speechSynthesisData().speakingPausedCompletionHandler = WTFMove(completionHandler); |
| speechSynthesisData().synthesizer->pause(); |
| } |
| |
| void WebPageProxy::speechSynthesisResume(CompletionHandler<void()>&& completionHandler) |
| { |
| speechSynthesisData().speakingResumedCompletionHandler = WTFMove(completionHandler); |
| speechSynthesisData().synthesizer->resume(); |
| } |
| #endif // ENABLE(SPEECH_SYNTHESIS) |
| |
| #if !PLATFORM(IOS_FAMILY) |
| |
| WebContentMode WebPageProxy::effectiveContentModeAfterAdjustingPolicies(API::WebsitePolicies&, const WebCore::ResourceRequest&) |
| { |
| return WebContentMode::Recommended; |
| } |
| |
| #endif // !PLATFORM(IOS_FAMILY) |
| |
| void WebPageProxy::addObserver(WebViewDidMoveToWindowObserver& observer) |
| { |
| auto result = m_webViewDidMoveToWindowObservers.add(&observer, observer); |
| ASSERT_UNUSED(result, result.isNewEntry); |
| } |
| |
| void WebPageProxy::removeObserver(WebViewDidMoveToWindowObserver& observer) |
| { |
| auto result = m_webViewDidMoveToWindowObservers.remove(&observer); |
| ASSERT_UNUSED(result, result); |
| } |
| |
| void WebPageProxy::webViewDidMoveToWindow() |
| { |
| auto observersCopy = m_webViewDidMoveToWindowObservers; |
| for (const auto& observer : observersCopy) { |
| if (!observer.value) |
| continue; |
| observer.value->webViewDidMoveToWindow(); |
| } |
| |
| auto newWindowKind = pageClient().windowKind(); |
| if (m_windowKind != newWindowKind) { |
| m_windowKind = newWindowKind; |
| if (m_drawingArea) |
| m_drawingArea->windowKindDidChange(); |
| } |
| } |
| |
| void WebPageProxy::setCanShowPlaceholder(const WebCore::ElementContext& context, bool canShowPlaceholder) |
| { |
| if (hasRunningProcess()) |
| send(Messages::WebPage::SetCanShowPlaceholder(context, canShowPlaceholder)); |
| } |
| |
| Logger& WebPageProxy::logger() |
| { |
| if (!m_logger) { |
| m_logger = Logger::create(this); |
| // FIXME: Does this really need to be disabled in ephemeral sessions? |
| m_logger->setEnabled(this, !sessionID().isEphemeral()); |
| } |
| |
| return *m_logger; |
| } |
| |
| void WebPageProxy::configureLoggingChannel(const String& channelName, WTFLogChannelState state, WTFLogLevel level) |
| { |
| #if !RELEASE_LOG_DISABLED |
| auto* channel = getLogChannel(channelName); |
| if (!channel) |
| return; |
| |
| channel->state = state; |
| channel->level = level; |
| #else |
| UNUSED_PARAM(channelName); |
| UNUSED_PARAM(state); |
| UNUSED_PARAM(level); |
| #endif |
| } |
| |
| #if HAVE(APP_SSO) |
| void WebPageProxy::decidePolicyForSOAuthorizationLoad(const String& extension, CompletionHandler<void(SOAuthorizationLoadPolicy)>&& completionHandler) |
| { |
| m_navigationClient->decidePolicyForSOAuthorizationLoad(*this, SOAuthorizationLoadPolicy::Allow, extension, WTFMove(completionHandler)); |
| } |
| #endif |
| |
| #if ENABLE(WEB_AUTHN) |
| void WebPageProxy::setMockWebAuthenticationConfiguration(MockWebAuthenticationConfiguration&& configuration) |
| { |
| m_websiteDataStore->setMockWebAuthenticationConfiguration(WTFMove(configuration)); |
| } |
| #endif |
| |
| void WebPageProxy::startTextManipulations(const Vector<WebCore::TextManipulationController::ExclusionRule>& exclusionRules, |
| TextManipulationItemCallback&& callback, WTF::CompletionHandler<void()>&& completionHandler) |
| { |
| if (!hasRunningProcess()) { |
| completionHandler(); |
| return; |
| } |
| m_textManipulationItemCallback = WTFMove(callback); |
| sendWithAsyncReply(Messages::WebPage::StartTextManipulations(exclusionRules), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::didFindTextManipulationItems(const Vector<WebCore::TextManipulationController::ManipulationItem>& items) |
| { |
| if (!m_textManipulationItemCallback) |
| return; |
| m_textManipulationItemCallback(items); |
| } |
| |
| void WebPageProxy::completeTextManipulation(const Vector<WebCore::TextManipulationController::ManipulationItem>& items, |
| WTF::Function<void(bool allFailed, const Vector<WebCore::TextManipulationController::ManipulationFailure>&)>&& completionHandler) |
| { |
| if (!hasRunningProcess()) { |
| completionHandler(true, { }); |
| return; |
| } |
| sendWithAsyncReply(Messages::WebPage::CompleteTextManipulation(items), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setCORSDisablingPatterns(Vector<String>&& patterns) |
| { |
| m_corsDisablingPatterns = WTFMove(patterns); |
| send(Messages::WebPage::UpdateCORSDisablingPatterns(m_corsDisablingPatterns)); |
| } |
| |
| void WebPageProxy::setOverriddenMediaType(const String& mediaType) |
| { |
| m_overriddenMediaType = mediaType; |
| send(Messages::WebPage::SetOverriddenMediaType(mediaType)); |
| } |
| |
| void WebPageProxy::setIsTakingSnapshotsForApplicationSuspension(bool isTakingSnapshotsForApplicationSuspension) |
| { |
| send(Messages::WebPage::SetIsTakingSnapshotsForApplicationSuspension(isTakingSnapshotsForApplicationSuspension)); |
| } |
| |
| void WebPageProxy::setNeedsDOMWindowResizeEvent() |
| { |
| send(Messages::WebPage::SetNeedsDOMWindowResizeEvent()); |
| } |
| |
| void WebPageProxy::loadServiceWorker(const URL& url, CompletionHandler<void(bool success)>&& completionHandler) |
| { |
| #if ENABLE(SERVICE_WORKER) |
| if (m_isClosed) |
| return completionHandler(false); |
| |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "loadServiceWorker:"); |
| |
| if (m_serviceWorkerLaunchCompletionHandler) |
| return completionHandler(false); |
| |
| m_isServiceWorkerPage = true; |
| m_serviceWorkerLaunchCompletionHandler = WTFMove(completionHandler); |
| |
| CString html = makeString("<script>navigator.serviceWorker.register('", url.string().utf8().data(), "');</script>").utf8(); |
| loadData({ reinterpret_cast<const uint8_t*>(html.data()), html.length() }, "text/html"_s, "UTF-8"_s, url.protocolHostAndPort()); |
| #else |
| UNUSED_PARAM(url); |
| completionHandler(false); |
| #endif |
| } |
| |
| #if !PLATFORM(IOS_FAMILY) |
| bool WebPageProxy::shouldForceForegroundPriorityForClientNavigation() const |
| { |
| return false; |
| } |
| #endif |
| |
| void WebPageProxy::getProcessDisplayName(CompletionHandler<void(String&&)>&& completionHandler) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetProcessDisplayName(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::setOrientationForMediaCapture(uint64_t orientation) |
| { |
| #if ENABLE(MEDIA_STREAM) |
| #if PLATFORM(COCOA) |
| if (auto* proxy = m_process->userMediaCaptureManagerProxy()) |
| proxy->setOrientation(orientation); |
| |
| auto* gpuProcess = m_process->processPool().gpuProcess(); |
| if (gpuProcess && preferences().captureVideoInGPUProcessEnabled()) |
| gpuProcess->setOrientationForMediaCapture(orientation); |
| #elif USE(GSTREAMER) |
| send(Messages::WebPage::SetOrientationForMediaCapture(orientation)); |
| #endif |
| #endif |
| } |
| |
| #if ENABLE(MEDIA_STREAM) && USE(GSTREAMER) |
| void WebPageProxy::setMockCaptureDevicesInterrupted(bool isCameraInterrupted, bool isMicrophoneInterrupted) |
| { |
| send(Messages::WebPage::SetMockCaptureDevicesInterrupted(isCameraInterrupted, isMicrophoneInterrupted)); |
| } |
| #endif |
| |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| void WebPageProxy::getLoadedSubresourceDomains(CompletionHandler<void(Vector<RegistrableDomain>&&)>&& completionHandler) |
| { |
| sendWithAsyncReply(Messages::WebPage::GetLoadedSubresourceDomains(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::clearLoadedSubresourceDomains() |
| { |
| send(Messages::WebPage::ClearLoadedSubresourceDomains()); |
| } |
| #endif |
| |
| #if ENABLE(GPU_PROCESS) |
| void WebPageProxy::gpuProcessDidFinishLaunching() |
| { |
| pageClient().gpuProcessDidFinishLaunching(); |
| } |
| |
| void WebPageProxy::gpuProcessExited(ProcessTerminationReason) |
| { |
| #if HAVE(VISIBILITY_PROPAGATION_VIEW) |
| m_contextIDForVisibilityPropagationInGPUProcess = 0; |
| #endif |
| |
| pageClient().gpuProcessDidExit(); |
| |
| #if ENABLE(MEDIA_STREAM) |
| bool activeAudioCapture = isCapturingAudio() && preferences().captureAudioInGPUProcessEnabled(); |
| bool activeVideoCapture = isCapturingVideo() && preferences().captureVideoInGPUProcessEnabled(); |
| bool activeDisplayCapture = false; |
| if (activeAudioCapture || activeVideoCapture) { |
| auto& gpuProcess = process().processPool().ensureGPUProcess(); |
| gpuProcess.updateCaptureAccess(activeAudioCapture, activeVideoCapture, activeDisplayCapture, m_process->coreProcessIdentifier(), [] { }); |
| #if PLATFORM(IOS_FAMILY) |
| gpuProcess.setOrientationForMediaCapture(m_deviceOrientation); |
| #endif |
| } |
| #endif |
| } |
| #endif |
| |
| #if ENABLE(CONTEXT_MENUS) && !PLATFORM(MAC) |
| |
| void WebPageProxy::platformDidSelectItemFromActiveContextMenu(const WebContextMenuItemData&) |
| { |
| } |
| |
| #endif |
| |
| #if !PLATFORM(COCOA) |
| |
| void WebPageProxy::willPerformPasteCommand(DOMPasteAccessCategory) |
| { |
| } |
| |
| #endif |
| |
| void WebPageProxy::dispatchActivityStateUpdateForTesting() |
| { |
| RunLoop::current().dispatch([protectedThis = Ref { *this }] { |
| protectedThis->dispatchActivityStateChange(); |
| }); |
| } |
| |
| void WebPageProxy::isLayerTreeFrozen(CompletionHandler<void(bool)>&& completionHandler) |
| { |
| sendWithAsyncReply(Messages::WebPage::IsLayerTreeFrozen(), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::requestSpeechRecognitionPermission(WebCore::SpeechRecognitionRequest& request, CompletionHandler<void(std::optional<SpeechRecognitionError>&&)>&& completionHandler) |
| { |
| if (!m_speechRecognitionPermissionManager) |
| m_speechRecognitionPermissionManager = makeUnique<SpeechRecognitionPermissionManager>(*this); |
| |
| m_speechRecognitionPermissionManager->request(request, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::requestSpeechRecognitionPermissionByDefaultAction(const WebCore::SecurityOriginData& origin, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| if (!m_speechRecognitionPermissionManager) { |
| completionHandler(false); |
| return; |
| } |
| |
| m_speechRecognitionPermissionManager->decideByDefaultAction(origin, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::requestUserMediaPermissionForSpeechRecognition(FrameIdentifier frameIdentifier, const WebCore::SecurityOrigin& requestingOrigin, const WebCore::SecurityOrigin& topOrigin, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| #if ENABLE(MEDIA_STREAM) |
| auto captureDevice = SpeechRecognitionCaptureSource::findCaptureDevice(); |
| if (!captureDevice) { |
| completionHandler(false); |
| return; |
| } |
| |
| userMediaPermissionRequestManager().checkUserMediaPermissionForSpeechRecognition(frameIdentifier, requestingOrigin, topOrigin, *captureDevice, WTFMove(completionHandler)); |
| #else |
| completionHandler(false); |
| #endif |
| } |
| |
| void WebPageProxy::requestMediaKeySystemPermissionByDefaultAction(const WebCore::SecurityOriginData& origin, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| completionHandler(true); |
| } |
| |
| #if ENABLE(MEDIA_STREAM) |
| |
| WebCore::CaptureSourceOrError WebPageProxy::createRealtimeMediaSourceForSpeechRecognition() |
| { |
| auto captureDevice = SpeechRecognitionCaptureSource::findCaptureDevice(); |
| if (!captureDevice) |
| return CaptureSourceOrError { "No device is available for capture"_s }; |
| |
| if (preferences().captureAudioInGPUProcessEnabled()) |
| return CaptureSourceOrError { SpeechRecognitionRemoteRealtimeMediaSource::create(m_process->ensureSpeechRecognitionRemoteRealtimeMediaSourceManager(), *captureDevice, m_webPageID) }; |
| |
| #if PLATFORM(IOS_FAMILY) |
| return CaptureSourceOrError { SpeechRecognitionRemoteRealtimeMediaSource::create(m_process->ensureSpeechRecognitionRemoteRealtimeMediaSourceManager(), *captureDevice, m_webPageID) }; |
| #else |
| return SpeechRecognitionCaptureSource::createRealtimeMediaSource(*captureDevice, m_webPageID); |
| #endif |
| } |
| |
| #endif |
| |
| #if HAVE(SCREEN_CAPTURE_KIT) |
| void WebPageProxy::setIndexOfGetDisplayMediaDeviceSelectedForTesting(std::optional<unsigned> index) |
| { |
| DisplayCaptureSessionManager::singleton().setIndexOfDeviceSelectedForTesting(index); |
| } |
| #endif |
| |
| #if ENABLE(ARKIT_INLINE_PREVIEW) |
| void WebPageProxy::modelElementGetCamera(ModelIdentifier modelIdentifier, CompletionHandler<void(Expected<WebCore::HTMLModelElementCamera, ResourceError>)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->getCameraForModelElement(modelIdentifier, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementSetCamera(ModelIdentifier modelIdentifier, WebCore::HTMLModelElementCamera camera, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->setCameraForModelElement(modelIdentifier, camera, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementIsPlayingAnimation(ModelIdentifier modelIdentifier, CompletionHandler<void(Expected<bool, ResourceError>)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->isPlayingAnimationForModelElement(modelIdentifier, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementSetAnimationIsPlaying(ModelIdentifier modelIdentifier, bool isPlaying, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->setAnimationIsPlayingForModelElement(modelIdentifier, isPlaying, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementIsLoopingAnimation(ModelIdentifier modelIdentifier, CompletionHandler<void(Expected<bool, ResourceError>)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->isLoopingAnimationForModelElement(modelIdentifier, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementSetIsLoopingAnimation(ModelIdentifier modelIdentifier, bool isLooping, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->setIsLoopingAnimationForModelElement(modelIdentifier, isLooping, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementAnimationDuration(ModelIdentifier modelIdentifier, CompletionHandler<void(Expected<Seconds, WebCore::ResourceError>)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->animationDurationForModelElement(modelIdentifier, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementAnimationCurrentTime(ModelIdentifier modelIdentifier, CompletionHandler<void(Expected<Seconds, WebCore::ResourceError>)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->animationCurrentTimeForModelElement(modelIdentifier, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementSetAnimationCurrentTime(ModelIdentifier modelIdentifier, Seconds currentTime, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->setAnimationCurrentTimeForModelElement(modelIdentifier, currentTime, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementHasAudio(ModelIdentifier modelIdentifier, CompletionHandler<void(Expected<bool, ResourceError>)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->hasAudioForModelElement(modelIdentifier, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementIsMuted(ModelIdentifier modelIdentifier, CompletionHandler<void(Expected<bool, ResourceError>)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->isMutedForModelElement(modelIdentifier, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementSetIsMuted(ModelIdentifier modelIdentifier, bool isMuted, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->setIsMutedForModelElement(modelIdentifier, isMuted, WTFMove(completionHandler)); |
| } |
| #endif |
| |
| #if ENABLE(ARKIT_INLINE_PREVIEW_IOS) |
| void WebPageProxy::takeModelElementFullscreen(ModelIdentifier modelIdentifier) |
| { |
| if (m_modelElementController) |
| m_modelElementController->takeModelElementFullscreen(modelIdentifier, URL { currentURL() }); |
| } |
| |
| void WebPageProxy::modelElementSetInteractionEnabled(ModelIdentifier modelIdentifier, bool isInteractionEnabled) |
| { |
| if (m_modelElementController) |
| m_modelElementController->setInteractionEnabledForModelElement(modelIdentifier, isInteractionEnabled); |
| } |
| |
| void WebPageProxy::modelInlinePreviewDidLoad(WebCore::GraphicsLayer::PlatformLayerID layerID) |
| { |
| send(Messages::WebPage::ModelInlinePreviewDidLoad(layerID)); |
| } |
| |
| void WebPageProxy::modelInlinePreviewDidFailToLoad(WebCore::GraphicsLayer::PlatformLayerID layerID, const WebCore::ResourceError& error) |
| { |
| send(Messages::WebPage::ModelInlinePreviewDidFailToLoad(layerID, error)); |
| } |
| |
| #endif |
| |
| #if ENABLE(ARKIT_INLINE_PREVIEW_MAC) |
| void WebPageProxy::modelElementCreateRemotePreview(const String& uuid, const FloatSize& size, CompletionHandler<void(Expected<std::pair<String, uint32_t>, ResourceError>)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->modelElementCreateRemotePreview(uuid, size, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementLoadRemotePreview(const String& uuid, const URL& url, CompletionHandler<void(std::optional<WebCore::ResourceError>&&)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->modelElementLoadRemotePreview(uuid, url, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::modelElementDestroyRemotePreview(const String& uuid) |
| { |
| if (m_modelElementController) |
| m_modelElementController->modelElementDestroyRemotePreview(uuid); |
| } |
| |
| void WebPageProxy::modelElementSizeDidChange(const String& uuid, WebCore::FloatSize size, CompletionHandler<void(Expected<MachSendRight, WebCore::ResourceError>)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->modelElementSizeDidChange(uuid, size, WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::handleMouseDownForModelElement(const String& uuid, const WebCore::LayoutPoint& flippedLocationInElement, MonotonicTime timestamp) |
| { |
| if (m_modelElementController) |
| m_modelElementController->handleMouseDownForModelElement(uuid, flippedLocationInElement, timestamp); |
| } |
| |
| void WebPageProxy::handleMouseMoveForModelElement(const String& uuid, const WebCore::LayoutPoint& flippedLocationInElement, MonotonicTime timestamp) |
| { |
| if (m_modelElementController) |
| m_modelElementController->handleMouseMoveForModelElement(uuid, flippedLocationInElement, timestamp); |
| } |
| |
| void WebPageProxy::handleMouseUpForModelElement(const String& uuid, const WebCore::LayoutPoint& flippedLocationInElement, MonotonicTime timestamp) |
| { |
| if (m_modelElementController) |
| m_modelElementController->handleMouseUpForModelElement(uuid, flippedLocationInElement, timestamp); |
| } |
| |
| void WebPageProxy::modelInlinePreviewUUIDs(CompletionHandler<void(Vector<String>&&)>&& completionHandler) |
| { |
| if (m_modelElementController) |
| m_modelElementController->inlinePreviewUUIDs(WTFMove(completionHandler)); |
| } |
| #endif |
| |
| #if !PLATFORM(COCOA) && !ENABLE(CONTENT_FILTERING_IN_NETWORKING_PROCESS) |
| Vector<SandboxExtension::Handle> WebPageProxy::createNetworkExtensionsSandboxExtensions(WebProcessProxy& process) |
| { |
| return { }; |
| } |
| |
| void WebPageProxy::classifyModalContainerControls(Vector<String>&&, CompletionHandler<void(Vector<ModalContainerControlType>&&)>&& completion) |
| { |
| completion({ }); |
| } |
| #endif |
| |
| #if ENABLE(MEDIA_SESSION_COORDINATOR) |
| void WebPageProxy::createMediaSessionCoordinator(Ref<MediaSessionCoordinatorProxyPrivate>&& privateCoordinator, CompletionHandler<void(bool)>&& completionHandler) |
| { |
| sendWithAsyncReply(Messages::WebPage::CreateMediaSessionCoordinator(privateCoordinator->identifier()), [weakThis = WeakPtr { *this }, privateCoordinator = WTFMove(privateCoordinator), completionHandler = WTFMove(completionHandler)](bool success) mutable { |
| |
| if (!weakThis || !success) { |
| completionHandler(false); |
| return; |
| } |
| |
| weakThis->m_mediaSessionCoordinatorProxy = RemoteMediaSessionCoordinatorProxy::create(*weakThis, WTFMove(privateCoordinator)); |
| completionHandler(true); |
| }); |
| } |
| #endif |
| |
| void WebPageProxy::requestScrollToRect(const FloatRect& targetRect, const FloatPoint& origin) |
| { |
| pageClient().requestScrollToRect(targetRect, origin); |
| } |
| |
| void WebPageProxy::scrollToRect(const FloatRect& targetRect, const FloatPoint& origin) |
| { |
| send(Messages::WebPage::ScrollToRect(targetRect, origin)); |
| } |
| |
| bool WebPageProxy::shouldEnableCaptivePortalMode() const |
| { |
| return m_configuration->captivePortalModeEnabled(); |
| } |
| |
| #if PLATFORM(COCOA) |
| void WebPageProxy::appPrivacyReportTestingData(CompletionHandler<void(const AppPrivacyReportTestingData&)>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::AppPrivacyReportTestingData(m_websiteDataStore->sessionID()), WTFMove(completionHandler)); |
| } |
| |
| void WebPageProxy::clearAppPrivacyReportTestingData(CompletionHandler<void()>&& completionHandler) |
| { |
| websiteDataStore().networkProcess().sendWithAsyncReply(Messages::NetworkProcess::ClearAppPrivacyReportTestingData(m_websiteDataStore->sessionID()), WTFMove(completionHandler)); |
| } |
| #endif |
| |
| void WebPageProxy::requestCookieConsent(CompletionHandler<void(CookieConsentDecisionResult)>&& completion) |
| { |
| m_uiClient->requestCookieConsent(WTFMove(completion)); |
| } |
| |
| void WebPageProxy::decidePolicyForModalContainer(OptionSet<ModalContainerControlType> types, CompletionHandler<void(ModalContainerDecision)>&& completion) |
| { |
| m_uiClient->decidePolicyForModalContainer(types, WTFMove(completion)); |
| } |
| |
| void WebPageProxy::beginTextRecognitionForVideoInElementFullScreen(MediaPlayerIdentifier identifier, FloatRect bounds) |
| { |
| if (!pageClient().isTextRecognitionInFullscreenVideoEnabled()) |
| return; |
| |
| #if ENABLE(GPU_PROCESS) |
| RefPtr gpuProcess = GPUProcessProxy::singletonIfCreated(); |
| if (!gpuProcess) |
| return; |
| |
| m_isPerformingTextRecognitionInElementFullScreen = true; |
| gpuProcess->requestBitmapImageForCurrentTime(m_process->coreProcessIdentifier(), identifier, [weakThis = WeakPtr { *this }, bounds](auto& bitmapHandle) { |
| RefPtr protectedThis = weakThis.get(); |
| if (!protectedThis || !protectedThis->m_isPerformingTextRecognitionInElementFullScreen) |
| return; |
| |
| protectedThis->pageClient().beginTextRecognitionForVideoInElementFullscreen(bitmapHandle, bounds); |
| protectedThis->m_isPerformingTextRecognitionInElementFullScreen = false; |
| }); |
| #else |
| UNUSED_PARAM(identifier); |
| UNUSED_PARAM(bounds); |
| #endif |
| } |
| |
| void WebPageProxy::cancelTextRecognitionForVideoInElementFullScreen() |
| { |
| if (!pageClient().isTextRecognitionInFullscreenVideoEnabled()) |
| return; |
| |
| m_isPerformingTextRecognitionInElementFullScreen = false; |
| pageClient().cancelTextRecognitionForVideoInElementFullscreen(); |
| } |
| |
| #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) |
| |
| void WebPageProxy::shouldAllowRemoveBackground(const ElementContext& context, CompletionHandler<void(bool)>&& completion) |
| { |
| sendWithAsyncReply(Messages::WebPage::ShouldAllowRemoveBackground(context), WTFMove(completion)); |
| } |
| |
| #endif |
| |
| #if HAVE(UIKIT_RESIZABLE_WINDOWS) |
| |
| void WebPageProxy::setIsWindowResizingEnabled(bool hasResizableWindows) |
| { |
| send(Messages::WebPage::SetIsWindowResizingEnabled(hasResizableWindows)); |
| } |
| |
| #endif |
| |
| bool WebPageProxy::shouldAvoidSynchronouslyWaitingToPreventDeadlock() const |
| { |
| if (m_isRunningModalJavaScriptDialog) |
| return true; |
| |
| #if ENABLE(GPU_PROCESS) |
| if (m_preferences->useGPUProcessForDOMRenderingEnabled()) { |
| auto* gpuProcess = GPUProcessProxy::singletonIfCreated(); |
| if (!gpuProcess || !gpuProcess->hasConnection()) { |
| // It's possible that the GPU process hasn't been initialized yet; in this case, we might end up in a deadlock |
| // if a message comes in from the web process to initialize the GPU process while we're synchronously waiting. |
| return true; |
| } |
| } |
| #endif // ENABLE(GPU_PROCESS) |
| |
| return false; |
| } |
| |
| } // namespace WebKit |
| |
| #undef WEBPAGEPROXY_RELEASE_LOG |
| #undef WEBPAGEPROXY_RELEASE_LOG_ERROR |
| #undef MESSAGE_CHECK_COMPLETION |
| #undef MESSAGE_CHECK_URL |
| #undef MESSAGE_CHECK |