| /* |
| * 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/RuntimeEnabledFeatures.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()) |
| m_process = relatedPage->ensureRunningProcess(); |
| 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(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; |
| 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()); |
| 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 |
| } |
| |
| enum class NavigationDirection { Backward, Forward }; |
| static WebBackForwardListItem* itemSkippingBackForwardItemsAddedByJSWithoutUserGesture(const WebBackForwardList& backForwardList, NavigationDirection direction) |
| { |
| auto delta = direction == NavigationDirection::Backward ? -1 : 1; |
| int itemIndex = delta; |
| auto* item = backForwardList.itemAtIndex(itemIndex); |
| if (!item) |
| return nullptr; |
| |
| #if PLATFORM(COCOA) |
| if (!linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::UIBackForwardSkipsHistoryItemsWithoutUserGesture)) |
| return item; |
| #endif |
| |
| auto* originalItem = item; |
| while (item->wasCreatedByJSWithoutUserInteraction()) { |
| itemIndex += delta; |
| item = backForwardList.itemAtIndex(itemIndex); |
| if (!item) |
| return originalItem; |
| RELEASE_LOG(Loading, "UI Navigation is skipping a WebBackForwardListItem because it was added by JavaScript without user interaction"); |
| } |
| return item; |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::goForward() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "goForward:"); |
| auto* forwardItem = itemSkippingBackForwardItemsAddedByJSWithoutUserGesture(m_backForwardList, NavigationDirection::Forward); |
| if (!forwardItem) |
| return nullptr; |
| |
| return goToBackForwardItem(*forwardItem, FrameLoadType::Forward); |
| } |
| |
| RefPtr<API::Navigation> WebPageProxy::goBack() |
| { |
| WEBPAGEPROXY_RELEASE_LOG(Loading, "goBack:"); |
| auto* backItem = itemSkippingBackForwardItemsAddedByJSWithoutUserGesture(m_backForwardList, NavigationDirection::Backward); |
| 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; |
| |
| 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; |
| 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); |
| if (flagsToUpdate & ActivityState::IsVisuallyIdle && pageClient().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) |
| 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 (m_potentiallyChangedActivityStateFlags & ActivityState::IsConnectedToHardwareConsole) |
| isConnectedToHardwareConsoleDidChange(); |
| |
| 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); |
| } |
| |
| void WebPageProxy::isConnectedToHardwareConsoleDidChange() |
| { |
| SetForScope<bool> isProcessing(m_isProcessingIsConnectedToHardwareConsoleDidChangeNotification, true); |
| if (m_process->isConnectedToHardwareConsole()) { |
| if (!m_captureWasMutedWhenHardwareConsoleDisconnected) |
| setMediaStreamCaptureMuted(false); |
| |
| m_captureWasMutedWhenHardwareConsoleDisconnected = false; |
| return; |
| } |
| |
| m_captureWasMutedWhenHardwareConsoleDisconnected = m_mutedState.containsAny(WebCore::MediaProducer::MediaStreamCaptureIsMuted); |
| setMediaStreamCaptureMuted(true); |
| } |
| |
| bool WebPageProxy::isAllowedToChangeMuteState() const |
| { |
| return m_isProcessingIsConnectedToHardwareConsoleDidChangeNotification || m_process->isConnectedToHardwareConsole(); |
| } |
| |
| 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) |
| { |
| 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(); |
|