/* * Copyright (C) 2010, 2011, 2015-2016 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 "APIContextMenuClient.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 "APISecurityOrigin.h" #include "APIUIClient.h" #include "APIURLRequest.h" #include "AuthenticationChallengeProxy.h" #include "AuthenticationDecisionListener.h" #include "DataReference.h" #include "DownloadProxy.h" #include "DrawingAreaProxy.h" #include "DrawingAreaProxyMessages.h" #include "EventDispatcherMessages.h" #include "LoadParameters.h" #include "Logging.h" #include "NativeWebGestureEvent.h" #include "NativeWebKeyboardEvent.h" #include "NativeWebMouseEvent.h" #include "NativeWebWheelEvent.h" #include "NavigationActionData.h" #include "NetworkProcessMessages.h" #include "NotificationPermissionRequest.h" #include "NotificationPermissionRequestManager.h" #include "PageClient.h" #include "PluginInformation.h" #include "PluginProcessManager.h" #include "PrintInfo.h" #include "TextChecker.h" #include "TextCheckerState.h" #include "UserMediaPermissionRequestProxy.h" #include "WKContextPrivate.h" #include "WebAutomationSession.h" #include "WebBackForwardList.h" #include "WebBackForwardListItem.h" #include "WebCertificateInfo.h" #include "WebContextMenuItem.h" #include "WebContextMenuProxy.h" #include "WebCoreArgumentCoders.h" #include "WebEditCommandProxy.h" #include "WebEvent.h" #include "WebEventConversion.h" #include "WebFormSubmissionListenerProxy.h" #include "WebFramePolicyListenerProxy.h" #include "WebFullScreenManagerProxy.h" #include "WebFullScreenManagerProxyMessages.h" #include "WebImage.h" #include "WebInspectorProxy.h" #include "WebInspectorUtilities.h" #include "WebNavigationState.h" #include "WebNotificationManagerProxy.h" #include "WebOpenPanelResultListenerProxy.h" #include "WebPageCreationParameters.h" #include "WebPageGroup.h" #include "WebPageGroupData.h" #include "WebPageMessages.h" #include "WebPageProxyMessages.h" #include "WebPaymentCoordinatorProxy.h" #include "WebPopupItem.h" #include "WebPopupMenuProxy.h" #include "WebPreferences.h" #include "WebProcessMessages.h" #include "WebProcessPool.h" #include "WebProcessProxy.h" #include "WebProtectionSpace.h" #include "WebUserContentControllerProxy.h" #include "WebsiteDataStore.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ENABLE(ASYNC_SCROLLING) #include "RemoteScrollingCoordinatorProxy.h" #endif #if ENABLE(VIBRATION) #include "WebVibrationProxy.h" #endif #ifndef NDEBUG #include #endif #if PLATFORM(COCOA) #include "RemoteLayerTreeDrawingAreaProxy.h" #include "RemoteLayerTreeScrollingPerformanceData.h" #include "ViewSnapshotStore.h" #include "WebVideoFullscreenManagerProxy.h" #include "WebVideoFullscreenManagerProxyMessages.h" #include #include #include #endif #if PLATFORM(GTK) #include "WebSelectionData.h" #endif #if USE(CAIRO) #include #endif #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS) #include #include #endif #if ENABLE(MEDIA_SESSION) #include "WebMediaSessionFocusManager.h" #include "WebMediaSessionMetadata.h" #include #endif #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) #include "WebPlaybackSessionManagerProxy.h" #endif #if ENABLE(MEDIA_STREAM) #include #endif // This controls what strategy we use for mouse wheel coalescing. #define MERGE_WHEEL_EVENTS 1 #define MESSAGE_CHECK(assertion) MESSAGE_CHECK_BASE(assertion, m_process->connection()) #define MESSAGE_CHECK_URL(url) MESSAGE_CHECK_BASE(m_process->checkURLReceivedFromWebProcess(url), m_process->connection()) #define RELEASE_LOG_IF_ALLOWED(...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), ProcessSuspension, __VA_ARGS__) using namespace WebCore; // Represents the number of wheel events we can hold in the queue before we start pushing them preemptively. static const unsigned wheelEventQueueSizeThreshold = 10; namespace WebKit { DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, webPageProxyCounter, ("WebPageProxy")); class ExceededDatabaseQuotaRecords { WTF_MAKE_NONCOPYABLE(ExceededDatabaseQuotaRecords); WTF_MAKE_FAST_ALLOCATED; friend class NeverDestroyed; public: struct Record { uint64_t frameID; String originIdentifier; String databaseName; String displayName; uint64_t currentQuota; uint64_t currentOriginUsage; uint64_t currentDatabaseUsage; uint64_t expectedUsage; RefPtr reply; }; static ExceededDatabaseQuotaRecords& singleton(); std::unique_ptr createRecord(uint64_t frameID, String originIdentifier, String databaseName, String displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, PassRefPtr); void add(std::unique_ptr); bool areBeingProcessed() const { return !!m_currentRecord; } Record* next(); private: ExceededDatabaseQuotaRecords() { } ~ExceededDatabaseQuotaRecords() { } Deque> m_records; std::unique_ptr m_currentRecord; }; ExceededDatabaseQuotaRecords& ExceededDatabaseQuotaRecords::singleton() { static NeverDestroyed records; return records; } std::unique_ptr ExceededDatabaseQuotaRecords::createRecord( uint64_t frameID, String originIdentifier, String databaseName, String displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, PassRefPtr reply) { auto record = std::make_unique(); record->frameID = frameID; record->originIdentifier = originIdentifier; record->databaseName = databaseName; record->displayName = displayName; record->currentQuota = currentQuota; record->currentOriginUsage = currentOriginUsage; record->currentDatabaseUsage = currentDatabaseUsage; record->expectedUsage = expectedUsage; record->reply = reply; return record; } void ExceededDatabaseQuotaRecords::add(std::unique_ptr record) { m_records.append(WTFMove(record)); } ExceededDatabaseQuotaRecords::Record* ExceededDatabaseQuotaRecords::next() { m_currentRecord = nullptr; if (!m_records.isEmpty()) m_currentRecord = m_records.takeFirst(); return m_currentRecord.get(); } #if !LOG_DISABLED 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 ""; } } #endif // !LOG_DISABLED class PageClientProtector { WTF_MAKE_NONCOPYABLE(PageClientProtector); public: PageClientProtector(PageClient& pageClient) : m_pageClient(pageClient) { m_pageClient.refView(); } ~PageClientProtector() { m_pageClient.derefView(); } private: PageClient& m_pageClient; }; Ref WebPageProxy::create(PageClient& pageClient, WebProcessProxy& process, uint64_t pageID, Ref&& configuration) { return adoptRef(*new WebPageProxy(pageClient, process, pageID, WTFMove(configuration))); } WebPageProxy::WebPageProxy(PageClient& pageClient, WebProcessProxy& process, uint64_t pageID, Ref&& configuration) : m_pageClient(pageClient) , m_configuration(WTFMove(configuration)) , m_loaderClient(std::make_unique()) , m_policyClient(std::make_unique()) , m_formClient(std::make_unique()) , m_uiClient(std::make_unique()) , m_findClient(std::make_unique()) , m_findMatchesClient(std::make_unique()) , m_diagnosticLoggingClient(std::make_unique()) #if ENABLE(CONTEXT_MENUS) , m_contextMenuClient(std::make_unique()) #endif , m_navigationState(std::make_unique()) , 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()->websiteDataStore()) , m_mainFrame(nullptr) , m_userAgent(standardUserAgent()) , m_overrideContentSecurityPolicy { m_configuration->overrideContentSecurityPolicy() } , m_treatsSHA1CertificatesAsInsecure(m_configuration->treatsSHA1SignedCertificatesAsInsecure()) #if ENABLE(FULLSCREEN_API) , m_fullscreenClient(std::make_unique()) #endif , m_geolocationPermissionRequestManager(*this) , m_notificationPermissionRequestManager(*this) , m_activityState(ActivityState::NoFlags) , m_viewWasEverInWindow(false) #if PLATFORM(IOS) , m_alwaysRunsAtForegroundPriority(m_configuration->alwaysRunsAtForegroundPriority()) #endif , m_initialCapitalizationEnabled(m_configuration->initialCapitalizationEnabled()) , m_backForwardList(WebBackForwardList::create(*this)) , m_maintainsInactiveSelection(false) , m_waitsForPaintAfterViewDidMoveToWindow(m_configuration->waitsForPaintAfterViewDidMoveToWindow()) , m_isEditable(false) , m_textZoomFactor(1) , m_pageZoomFactor(1) , m_pageScaleFactor(1) , m_pluginZoomFactor(1) , m_pluginScaleFactor(1) , m_intrinsicDeviceScaleFactor(1) , m_customDeviceScaleFactor(0) , m_topContentInset(0) , m_layerHostingMode(LayerHostingMode::InProcess) , m_drawsBackground(true) , m_useFixedLayout(false) , m_suppressScrollbarAnimations(false) , m_paginationMode(Pagination::Unpaginated) , m_paginationBehavesLikeColumns(false) , m_pageLength(0) , m_gapBetweenPages(0) , m_paginationLineGridEnabled(false) , m_isValid(true) , m_isClosed(false) , m_canRunModal(false) , m_isInPrintingMode(false) , m_isPerformingDOMPrintOperation(false) , m_inDecidePolicyForResponseSync(false) , m_decidePolicyForResponseRequest(0) , m_syncMimeTypePolicyActionIsValid(false) , m_syncMimeTypePolicyAction(PolicyUse) , m_syncMimeTypePolicyDownloadID(0) , m_inDecidePolicyForNavigationAction(false) , m_syncNavigationActionPolicyActionIsValid(false) , m_syncNavigationActionPolicyAction(PolicyUse) , m_syncNavigationActionPolicyDownloadID(0) , m_processingMouseMoveEvent(false) , m_pageID(pageID) , m_sessionID(m_configuration->sessionID()) , m_isPageSuspended(false) , m_addsVisitedLinks(true) , m_controlledByAutomation(m_configuration->isControlledByAutomation()) #if ENABLE(REMOTE_INSPECTOR) , m_allowsRemoteInspection(true) #endif #if PLATFORM(COCOA) , m_isSmartInsertDeleteEnabled(TextChecker::isSmartInsertDeleteEnabled()) #endif #if PLATFORM(GTK) , m_backgroundColor(Color::white) #endif , m_spellDocumentTag(0) , m_hasSpellDocumentTag(false) , m_pendingLearnOrIgnoreWordMessageCount(0) , m_mainFrameHasCustomContentProvider(false) #if ENABLE(DRAG_SUPPORT) , m_currentDragOperation(DragOperationNone) , m_currentDragIsOverFileInput(false) , m_currentDragNumberOfFilesToBeAccepted(0) #endif , m_pageLoadState(*this) , m_delegatesScrolling(false) , m_mainFrameHasHorizontalScrollbar(false) , m_mainFrameHasVerticalScrollbar(false) , m_canShortCircuitHorizontalWheelEvents(true) , m_mainFrameIsPinnedToLeftSide(true) , m_mainFrameIsPinnedToRightSide(true) , m_mainFrameIsPinnedToTopSide(true) , m_mainFrameIsPinnedToBottomSide(true) , m_shouldUseImplicitRubberBandControl(false) , m_rubberBandsAtLeft(true) , m_rubberBandsAtRight(true) , m_rubberBandsAtTop(true) , m_rubberBandsAtBottom(true) , m_enableVerticalRubberBanding(true) , m_enableHorizontalRubberBanding(true) , m_backgroundExtendsBeyondPage(false) , m_shouldRecordNavigationSnapshots(false) , m_isShowingNavigationGestureSnapshot(false) , m_pageCount(0) , m_renderTreeSize(0) , m_sessionRestorationRenderTreeSize(0) , m_hitRenderTreeSizeThreshold(false) , m_suppressVisibilityUpdates(false) , m_autoSizingShouldExpandToViewHeight(false) , m_mediaVolume(1) , m_mayStartMediaWhenInWindow(true) , m_waitingForDidUpdateActivityState(false) #if PLATFORM(COCOA) , m_scrollPerformanceDataCollectionEnabled(false) #endif , m_scrollPinningBehavior(DoNotPin) , m_navigationID(0) , m_configurationPreferenceValues(m_configuration->preferenceValues()) , m_potentiallyChangedActivityStateFlags(ActivityState::NoFlags) , m_activityStateChangeWantsSynchronousReply(false) , m_weakPtrFactory(this) { m_webProcessLifetimeTracker.addObserver(m_visitedLinkStore); m_webProcessLifetimeTracker.addObserver(m_websiteDataStore); updateActivityState(); updateThrottleState(); updateHiddenPageThrottlingAutoIncreases(); #if HAVE(OUT_OF_PROCESS_LAYER_HOSTING) m_layerHostingMode = m_activityState & ActivityState::IsInWindow ? m_pageClient.viewLayerHostingMode() : LayerHostingMode::OutOfProcess; #endif platformInitialize(); #ifndef NDEBUG webPageProxyCounter.increment(); #endif WebProcessPool::statistics().wkPageCount++; m_preferences->addPage(*this); m_pageGroup->addPage(this); m_inspector = WebInspectorProxy::create(this); #if ENABLE(FULLSCREEN_API) m_fullScreenManager = WebFullScreenManagerProxy::create(*this, m_pageClient.fullScreenManagerProxyClient()); #endif #if PLATFORM(IOS) && HAVE(AVKIT) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) m_playbackSessionManager = WebPlaybackSessionManagerProxy::create(*this); m_videoFullscreenManager = WebVideoFullscreenManagerProxy::create(*this, *m_playbackSessionManager); #endif #if ENABLE(VIBRATION) m_vibration = WebVibrationProxy::create(this); #endif #if ENABLE(APPLE_PAY) m_paymentCoordinator = std::make_unique(*this); #endif m_process->addMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID, *this); if (m_sessionID.isEphemeral()) { m_process->processPool().sendToNetworkingProcess(Messages::NetworkProcess::EnsurePrivateBrowsingSession(m_sessionID)); m_process->processPool().sendToAllProcesses(Messages::WebProcess::EnsurePrivateBrowsingSession(m_sessionID)); } #if PLATFORM(COCOA) const CFIndex activityStateChangeRunLoopOrder = (CFIndex)RunLoopObserver::WellKnownRunLoopOrders::CoreAnimationCommit - 1; m_activityStateChangeDispatcher = std::make_unique(activityStateChangeRunLoopOrder, [this] { this->dispatchActivityStateChange(); }); #endif } WebPageProxy::~WebPageProxy() { ASSERT(m_process->webPage(m_pageID) != this); #if !ASSERT_DISABLED for (WebPageProxy* page : m_process->pages()) ASSERT(page != this); #endif if (!m_isClosed) close(); WebProcessPool::statistics().wkPageCount--; if (m_hasSpellDocumentTag) TextChecker::closeSpellDocumentWithTag(m_spellDocumentTag); m_preferences->removePage(*this); m_pageGroup->removePage(this); #ifndef NDEBUG webPageProxyCounter.decrement(); #endif } const API::PageConfiguration& WebPageProxy::configuration() const { return m_configuration.get(); } pid_t WebPageProxy::processIdentifier() const { if (m_isClosed) return 0; return m_process->processIdentifier(); } bool WebPageProxy::isValid() const { // A page that has been explicitly closed is never valid. if (m_isClosed) return false; return m_isValid; } 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(std::unique_ptr historyClient) { m_historyClient = WTFMove(historyClient); } void WebPageProxy::setNavigationClient(std::unique_ptr navigationClient) { m_navigationClient = WTFMove(navigationClient); } void WebPageProxy::setLoaderClient(std::unique_ptr loaderClient) { if (!loaderClient) { m_loaderClient = std::make_unique(); return; } m_loaderClient = WTFMove(loaderClient); } void WebPageProxy::setPolicyClient(std::unique_ptr policyClient) { if (!policyClient) { m_policyClient = std::make_unique(); return; } m_policyClient = WTFMove(policyClient); } void WebPageProxy::setFormClient(std::unique_ptr formClient) { if (!formClient) { m_formClient = std::make_unique(); return; } m_formClient = WTFMove(formClient); } void WebPageProxy::setUIClient(std::unique_ptr uiClient) { if (!uiClient) { m_uiClient = std::make_unique(); return; } m_uiClient = WTFMove(uiClient); if (!isValid()) return; m_process->send(Messages::WebPage::SetCanRunBeforeUnloadConfirmPanel(m_uiClient->canRunBeforeUnloadConfirmPanel()), m_pageID); setCanRunModal(m_uiClient->canRunModal()); } void WebPageProxy::setIconLoadingClient(std::unique_ptr iconLoadingClient) { bool hasClient = iconLoadingClient.get(); if (!iconLoadingClient) m_iconLoadingClient = std::make_unique(); else m_iconLoadingClient = WTFMove(iconLoadingClient); if (!isValid()) return; m_process->send(Messages::WebPage::SetUseIconLoadingClient(hasClient), m_pageID); } void WebPageProxy::setFindClient(std::unique_ptr findClient) { if (!findClient) { m_findClient = std::make_unique(); return; } m_findClient = WTFMove(findClient); } void WebPageProxy::setFindMatchesClient(std::unique_ptr findMatchesClient) { if (!findMatchesClient) { m_findMatchesClient = std::make_unique(); return; } m_findMatchesClient = WTFMove(findMatchesClient); } void WebPageProxy::setDiagnosticLoggingClient(std::unique_ptr diagnosticLoggingClient) { if (!diagnosticLoggingClient) { m_diagnosticLoggingClient = std::make_unique(); return; } m_diagnosticLoggingClient = WTFMove(diagnosticLoggingClient); } #if ENABLE(CONTEXT_MENUS) void WebPageProxy::setContextMenuClient(std::unique_ptr contextMenuClient) { if (!contextMenuClient) { m_contextMenuClient = std::make_unique(); return; } m_contextMenuClient = WTFMove(contextMenuClient); } #endif void WebPageProxy::setInjectedBundleClient(const WKPageInjectedBundleClientBase* client) { if (!client) { m_injectedBundleClient = nullptr; return; } m_injectedBundleClient = std::make_unique(); m_injectedBundleClient->initialize(client); } 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, UserData& returnUserData) { ASSERT(m_process->connection() == &connection); if (!m_injectedBundleClient) return; RefPtr returnData; m_injectedBundleClient->didReceiveSynchronousMessageFromInjectedBundle(this, messageName, m_process->transformHandlesToObjects(messageBody.object()).get(), returnData); returnUserData = UserData(m_process->transformObjectsToHandles(returnData.get())); } void WebPageProxy::reattachToWebProcess() { ASSERT(!m_isClosed); ASSERT(!isValid()); ASSERT(m_process->state() == WebProcessProxy::State::Terminated); m_isValid = true; m_wasKilledForBeingUnresponsiveWhileInBackground = false; m_process->removeWebPage(m_pageID); m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID); m_process = m_process->processPool().createNewWebProcessRespectingProcessCountLimit(); ASSERT(m_process->state() != ChildProcessProxy::State::Terminated); if (m_process->state() == ChildProcessProxy::State::Running) processDidFinishLaunching(); m_process->addExistingWebPage(this, m_pageID); m_process->addMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID, *this); updateActivityState(); updateThrottleState(); m_inspector = WebInspectorProxy::create(this); #if ENABLE(FULLSCREEN_API) m_fullScreenManager = WebFullScreenManagerProxy::create(*this, m_pageClient.fullScreenManagerProxyClient()); #endif #if PLATFORM(IOS) && HAVE(AVKIT) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) m_playbackSessionManager = WebPlaybackSessionManagerProxy::create(*this); m_videoFullscreenManager = WebVideoFullscreenManagerProxy::create(*this, *m_playbackSessionManager); #endif #if ENABLE(APPLE_PAY) m_paymentCoordinator = std::make_unique(*this); #endif initializeWebPage(); m_pageClient.didRelaunchProcess(); m_drawingArea->waitForBackingStoreUpdateOnNextPaint(); } RefPtr WebPageProxy::reattachToWebProcessForReload() { if (m_isClosed) return nullptr; ASSERT(!isValid()); reattachToWebProcess(); if (!m_backForwardList->currentItem()) return nullptr; auto navigation = m_navigationState->createReloadNavigation(); // We allow stale content when reloading a WebProcess that's been killed or crashed. m_process->send(Messages::WebPage::GoToBackForwardItem(navigation->navigationID(), m_backForwardList->currentItem()->itemID()), m_pageID); m_process->responsivenessTimer().start(); return WTFMove(navigation); } RefPtr WebPageProxy::reattachToWebProcessWithItem(WebBackForwardListItem* item) { if (m_isClosed) return nullptr; ASSERT(!isValid()); reattachToWebProcess(); if (!item) return nullptr; if (item != m_backForwardList->currentItem()) m_backForwardList->goToItem(item); auto navigation = m_navigationState->createBackForwardNavigation(); m_process->send(Messages::WebPage::GoToBackForwardItem(navigation->navigationID(), item->itemID()), m_pageID); m_process->responsivenessTimer().start(); return WTFMove(navigation); } void WebPageProxy::initializeWebPage() { ASSERT(isValid()); BackForwardListItemVector items = m_backForwardList->entries(); for (size_t i = 0; i < items.size(); ++i) m_process->registerNewWebBackForwardListItem(items[i].get()); m_drawingArea = m_pageClient.createDrawingAreaProxy(); ASSERT(m_drawingArea); #if ENABLE(ASYNC_SCROLLING) if (m_drawingArea->type() == DrawingAreaTypeRemoteLayerTree) { m_scrollingCoordinatorProxy = std::make_unique(*this); #if PLATFORM(IOS) // On iOS, main frame scrolls are sent in terms of visible rect updates. m_scrollingCoordinatorProxy->setPropagatesMainFrameScrolls(false); #endif } #endif #if ENABLE(INSPECTOR_SERVER) if (m_preferences->developerExtrasEnabled()) inspector()->enableRemoteInspection(); #endif process().send(Messages::WebProcess::CreateWebPage(m_pageID, creationParameters()), 0); m_needsToFinishInitializingWebPageAfterProcessLaunch = true; finishInitializingWebPageAfterProcessLaunch(); } void WebPageProxy::finishInitializingWebPageAfterProcessLaunch() { if (!m_needsToFinishInitializingWebPageAfterProcessLaunch) return; if (m_process->state() != WebProcessProxy::State::Running) return; m_needsToFinishInitializingWebPageAfterProcessLaunch = false; m_process->addWebUserContentControllerProxy(m_userContentController); m_process->addVisitedLinkStore(m_visitedLinkStore); } void WebPageProxy::close() { if (m_isClosed) return; m_isClosed = true; if (m_activePopupMenu) m_activePopupMenu->cancelTracking(); #if ENABLE(CONTEXT_MENUS) m_activeContextMenu = nullptr; #endif m_backForwardList->pageClosed(); m_pageClient.pageClosed(); m_process->disconnectFramesFromPage(this); resetState(ResetStateReason::PageInvalidated); m_loaderClient = std::make_unique(); m_navigationClient = nullptr; m_policyClient = std::make_unique(); m_formClient = std::make_unique(); m_uiClient = std::make_unique(); m_findClient = std::make_unique(); m_findMatchesClient = std::make_unique(); m_diagnosticLoggingClient = std::make_unique(); #if ENABLE(CONTEXT_MENUS) m_contextMenuClient = std::make_unique(); #endif m_webProcessLifetimeTracker.pageWasInvalidated(); m_process->send(Messages::WebPage::Close(), m_pageID); m_process->removeWebPage(m_pageID); m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID); m_process->processPool().supplement()->clearNotifications(this); // Null out related WebPageProxy to avoid leaks. m_configuration->setRelatedPage(nullptr); #if PLATFORM(IOS) // Make sure we don't hold a process assertion after getting closed. m_activityToken = nullptr; #endif } bool WebPageProxy::tryClose() { if (!isValid()) return true; // 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_process->send(Messages::WebPage::TryClose(), m_pageID); m_process->responsivenessTimer().start(); return false; } bool WebPageProxy::maybeInitializeSandboxExtensionHandle(const URL& url, SandboxExtension::Handle& sandboxExtensionHandle) { if (!url.isLocalFile()) return false; if (m_process->hasAssumedReadAccessToURL(url)) return false; // Inspector resources are in a directory with assumed access. ASSERT_WITH_SECURITY_IMPLICATION(!WebKit::isInspectorPage(*this)); SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle); return true; } #if !PLATFORM(COCOA) void WebPageProxy::addPlatformLoadParameters(LoadParameters&) { } #endif RefPtr WebPageProxy::loadRequest(const ResourceRequest& request, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData) { if (m_isClosed) return nullptr; auto navigation = m_navigationState->createLoadRequestNavigation(request); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, request.url()); if (!isValid()) reattachToWebProcess(); LoadParameters loadParameters; loadParameters.navigationID = navigation->navigationID(); loadParameters.request = request; loadParameters.shouldOpenExternalURLsPolicy = (uint64_t)shouldOpenExternalURLsPolicy; loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); bool createdExtension = maybeInitializeSandboxExtensionHandle(request.url(), loadParameters.sandboxExtensionHandle); if (createdExtension) m_process->willAcquireUniversalFileReadSandboxExtension(); addPlatformLoadParameters(loadParameters); m_process->send(Messages::WebPage::LoadRequest(loadParameters), m_pageID); m_process->responsivenessTimer().start(); return WTFMove(navigation); } RefPtr WebPageProxy::loadFile(const String& fileURLString, const String& resourceDirectoryURLString, API::Object* userData) { if (m_isClosed) return nullptr; if (!isValid()) reattachToWebProcess(); URL fileURL = URL(URL(), fileURLString); if (!fileURL.isLocalFile()) return nullptr; URL resourceDirectoryURL; if (resourceDirectoryURLString.isNull()) resourceDirectoryURL = URL(ParsedURLString, ASCIILiteral("file:///")); else { resourceDirectoryURL = URL(URL(), resourceDirectoryURLString); if (!resourceDirectoryURL.isLocalFile()) return nullptr; } auto navigation = m_navigationState->createLoadRequestNavigation(ResourceRequest(fileURL)); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, fileURLString); String resourceDirectoryPath = resourceDirectoryURL.fileSystemPath(); LoadParameters loadParameters; loadParameters.navigationID = navigation->navigationID(); loadParameters.request = fileURL; loadParameters.shouldOpenExternalURLsPolicy = (uint64_t)ShouldOpenExternalURLsPolicy::ShouldNotAllow; loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); SandboxExtension::createHandle(resourceDirectoryPath, SandboxExtension::ReadOnly, loadParameters.sandboxExtensionHandle); addPlatformLoadParameters(loadParameters); m_process->assumeReadAccessToBaseURL(resourceDirectoryURL); m_process->send(Messages::WebPage::LoadRequest(loadParameters), m_pageID); m_process->responsivenessTimer().start(); return WTFMove(navigation); } RefPtr WebPageProxy::loadData(API::Data* data, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData) { if (m_isClosed) return nullptr; auto navigation = m_navigationState->createLoadDataNavigation(); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, !baseURL.isEmpty() ? baseURL : blankURL().string()); if (!isValid()) reattachToWebProcess(); LoadParameters loadParameters; loadParameters.navigationID = navigation->navigationID(); loadParameters.data = data->dataReference(); loadParameters.MIMEType = MIMEType; loadParameters.encodingName = encoding; loadParameters.baseURLString = baseURL; loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); addPlatformLoadParameters(loadParameters); m_process->assumeReadAccessToBaseURL(baseURL); m_process->send(Messages::WebPage::LoadData(loadParameters), m_pageID); m_process->responsivenessTimer().start(); return WTFMove(navigation); } // FIXME: Get rid of loadHTMLString and just use loadData instead. RefPtr WebPageProxy::loadHTMLString(const String& htmlString, const String& baseURL, API::Object* userData) { if (m_isClosed) return nullptr; auto navigation = m_navigationState->createLoadDataNavigation(); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, !baseURL.isEmpty() ? baseURL : blankURL().string()); if (!isValid()) reattachToWebProcess(); LoadParameters loadParameters; loadParameters.navigationID = navigation->navigationID(); loadParameters.string = htmlString; loadParameters.MIMEType = ASCIILiteral("text/html"); loadParameters.baseURLString = baseURL; loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); addPlatformLoadParameters(loadParameters); m_process->assumeReadAccessToBaseURL(baseURL); m_process->send(Messages::WebPage::LoadString(loadParameters), m_pageID); m_process->responsivenessTimer().start(); return WTFMove(navigation); } void WebPageProxy::loadAlternateHTMLString(const String& htmlString, const String& baseURL, const String& unreachableURL, API::Object* userData) { // 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) return; if (!m_failingProvisionalLoadURL.isEmpty()) m_isLoadingAlternateHTMLStringForFailingProvisionalLoad = true; if (!isValid()) reattachToWebProcess(); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, unreachableURL); m_pageLoadState.setUnreachableURL(transaction, unreachableURL); if (m_mainFrame) m_mainFrame->setUnreachableURL(unreachableURL); LoadParameters loadParameters; loadParameters.navigationID = 0; loadParameters.string = htmlString; loadParameters.baseURLString = baseURL; loadParameters.unreachableURLString = unreachableURL; loadParameters.provisionalLoadErrorURLString = m_failingProvisionalLoadURL; loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); addPlatformLoadParameters(loadParameters); m_process->assumeReadAccessToBaseURL(baseURL); m_process->assumeReadAccessToBaseURL(unreachableURL); m_process->send(Messages::WebPage::LoadAlternateHTMLString(loadParameters), m_pageID); m_process->responsivenessTimer().start(); } void WebPageProxy::loadPlainTextString(const String& string, API::Object* userData) { if (m_isClosed) return; if (!isValid()) reattachToWebProcess(); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, blankURL().string()); LoadParameters loadParameters; loadParameters.navigationID = 0; loadParameters.string = string; loadParameters.MIMEType = ASCIILiteral("text/plain"); loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); addPlatformLoadParameters(loadParameters); m_process->send(Messages::WebPage::LoadString(loadParameters), m_pageID); m_process->responsivenessTimer().start(); } void WebPageProxy::loadWebArchiveData(API::Data* webArchiveData, API::Object* userData) { if (m_isClosed) return; if (!isValid()) reattachToWebProcess(); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, blankURL().string()); LoadParameters loadParameters; loadParameters.navigationID = 0; loadParameters.data = webArchiveData->dataReference(); loadParameters.MIMEType = ASCIILiteral("application/x-webarchive"); loadParameters.encodingName = ASCIILiteral("utf-16"); loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get()); addPlatformLoadParameters(loadParameters); m_process->send(Messages::WebPage::LoadData(loadParameters), m_pageID); m_process->responsivenessTimer().start(); } void WebPageProxy::navigateToPDFLinkWithSimulatedClick(const String& url, IntPoint documentPoint, IntPoint screenPoint) { if (m_isClosed) return; if (WebCore::protocolIsJavaScript(url)) return; if (!isValid()) reattachToWebProcess(); m_process->send(Messages::WebPage::NavigateToPDFLinkWithSimulatedClick(url, documentPoint, screenPoint), m_pageID); m_process->responsivenessTimer().start(); } void WebPageProxy::stopLoading() { if (!isValid()) return; m_process->send(Messages::WebPage::StopLoading(), m_pageID); m_process->responsivenessTimer().start(); } RefPtr WebPageProxy::reload(bool reloadFromOrigin, bool contentBlockersEnabled) { SandboxExtension::Handle sandboxExtensionHandle; String url = m_pageLoadState.activeURL(); if (url.isEmpty() && m_backForwardList->currentItem()) url = m_backForwardList->currentItem()->url(); if (!url.isEmpty()) { auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, url); // We may not have an extension yet if back/forward list was reinstated after a WebProcess crash or a browser relaunch bool createdExtension = maybeInitializeSandboxExtensionHandle(URL(URL(), url), sandboxExtensionHandle); if (createdExtension) m_process->willAcquireUniversalFileReadSandboxExtension(); } if (!isValid()) return reattachToWebProcessForReload(); auto navigation = m_navigationState->createReloadNavigation(); m_process->send(Messages::WebPage::Reload(navigation->navigationID(), reloadFromOrigin, contentBlockersEnabled, sandboxExtensionHandle), m_pageID); m_process->responsivenessTimer().start(); return WTFMove(navigation); } void WebPageProxy::recordAutomaticNavigationSnapshot() { if (m_suppressAutomaticNavigationSnapshotting) return; if (WebBackForwardListItem* item = m_backForwardList->currentItem()) recordNavigationSnapshot(*item); } void WebPageProxy::recordNavigationSnapshot(WebBackForwardListItem& item) { if (!m_shouldRecordNavigationSnapshots) return; #if PLATFORM(COCOA) ViewSnapshotStore::singleton().recordSnapshot(*this, item); #else UNUSED_PARAM(item); #endif } RefPtr WebPageProxy::goForward() { WebBackForwardListItem* forwardItem = m_backForwardList->forwardItem(); if (!forwardItem) return nullptr; auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, forwardItem->url()); if (!isValid()) return reattachToWebProcessWithItem(forwardItem); RefPtr navigation; if (!m_backForwardList->currentItem()->itemIsInSameDocument(*forwardItem)) navigation = m_navigationState->createBackForwardNavigation(); m_process->send(Messages::WebPage::GoForward(navigation ? navigation->navigationID() : 0, forwardItem->itemID()), m_pageID); m_process->responsivenessTimer().start(); return navigation; } RefPtr WebPageProxy::goBack() { WebBackForwardListItem* backItem = m_backForwardList->backItem(); if (!backItem) return nullptr; auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, backItem->url()); if (!isValid()) return reattachToWebProcessWithItem(backItem); RefPtr navigation; if (!m_backForwardList->currentItem()->itemIsInSameDocument(*backItem)) navigation = m_navigationState->createBackForwardNavigation(); m_process->send(Messages::WebPage::GoBack(navigation ? navigation->navigationID() : 0, backItem->itemID()), m_pageID); m_process->responsivenessTimer().start(); return navigation; } RefPtr WebPageProxy::goToBackForwardItem(WebBackForwardListItem* item) { if (!isValid()) return reattachToWebProcessWithItem(item); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setPendingAPIRequestURL(transaction, item->url()); RefPtr navigation; if (!m_backForwardList->currentItem()->itemIsInSameDocument(*item)) navigation = m_navigationState->createBackForwardNavigation(); m_process->send(Messages::WebPage::GoToBackForwardItem(navigation ? navigation->navigationID() : 0, item->itemID()), m_pageID); m_process->responsivenessTimer().start(); return navigation; } void WebPageProxy::tryRestoreScrollPosition() { if (!isValid()) return; m_process->send(Messages::WebPage::TryRestoreScrollPosition(), m_pageID); } void WebPageProxy::didChangeBackForwardList(WebBackForwardListItem* added, Vector> removed) { PageClientProtector protector(m_pageClient); 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(uint64_t itemID, const UserData& userData) { PageClientProtector protector(m_pageClient); if (WebBackForwardListItem* item = m_process->webBackForwardItem(itemID)) m_loaderClient->willGoToBackForwardListItem(*this, item, m_process->transformHandlesToObjects(userData.object()).get()); } bool WebPageProxy::shouldKeepCurrentBackForwardListItemInList(WebBackForwardListItem* item) { PageClientProtector protector(m_pageClient); return m_loaderClient->shouldKeepCurrentBackForwardListItemInList(*this, item); } bool WebPageProxy::canShowMIMEType(const String& mimeType) { if (MIMETypeRegistry::canShowMIMEType(mimeType)) return true; #if ENABLE(NETSCAPE_PLUGIN_API) String newMimeType = mimeType; PluginModuleInfo plugin = m_process->processPool().pluginInfoStore().findPlugin(newMimeType, URL()); if (!plugin.path.isNull() && m_preferences->pluginsEnabled()) return true; #endif // ENABLE(NETSCAPE_PLUGIN_API) #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 (isValid()) m_process->send(Messages::WebPage::SetControlledByAutomation(controlled), m_pageID); } #if ENABLE(REMOTE_INSPECTOR) void WebPageProxy::setAllowsRemoteInspection(bool allow) { if (m_allowsRemoteInspection == allow) return; m_allowsRemoteInspection = allow; if (isValid()) m_process->send(Messages::WebPage::SetAllowsRemoteInspection(allow), m_pageID); } void WebPageProxy::setRemoteInspectionNameOverride(const String& name) { if (m_remoteInspectionNameOverride == name) return; m_remoteInspectionNameOverride = name; if (isValid()) m_process->send(Messages::WebPage::SetRemoteInspectionNameOverride(m_remoteInspectionNameOverride), m_pageID); } #endif void WebPageProxy::setDrawsBackground(bool drawsBackground) { if (m_drawsBackground == drawsBackground) return; m_drawsBackground = drawsBackground; if (isValid()) m_process->send(Messages::WebPage::SetDrawsBackground(drawsBackground), m_pageID); } void WebPageProxy::setTopContentInset(float contentInset) { if (m_topContentInset == contentInset) return; m_topContentInset = contentInset; if (!isValid()) return; #if HAVE(COREANIMATION_FENCES) MachSendRight fence = m_drawingArea->createFence(); auto fenceAttachment = IPC::Attachment(fence.leakSendRight(), MACH_MSG_TYPE_MOVE_SEND); m_process->send(Messages::WebPage::SetTopContentInsetFenced(contentInset, fenceAttachment), m_pageID); #else m_process->send(Messages::WebPage::SetTopContentInset(contentInset), m_pageID); #endif } void WebPageProxy::setUnderlayColor(const Color& color) { if (m_underlayColor == color) return; m_underlayColor = color; if (isValid()) m_process->send(Messages::WebPage::SetUnderlayColor(color), m_pageID); } void WebPageProxy::viewWillStartLiveResize() { if (!isValid()) return; #if ENABLE(INPUT_TYPE_COLOR_POPOVER) && ENABLE(INPUT_TYPE_COLOR) if (m_colorPicker) endColorPicker(); #endif m_process->send(Messages::WebPage::ViewWillStartLiveResize(), m_pageID); } void WebPageProxy::viewWillEndLiveResize() { if (!isValid()) return; m_process->send(Messages::WebPage::ViewWillEndLiveResize(), m_pageID); } void WebPageProxy::setViewNeedsDisplay(const Region& region) { m_pageClient.setViewNeedsDisplay(region); } void WebPageProxy::requestScroll(const FloatPoint& scrollPosition, const IntPoint& scrollOrigin, bool isProgrammaticScroll) { m_pageClient.requestScroll(scrollPosition, scrollOrigin, isProgrammaticScroll); } void WebPageProxy::setSuppressVisibilityUpdates(bool flag) { if (m_suppressVisibilityUpdates == flag) return; m_suppressVisibilityUpdates = flag; if (!m_suppressVisibilityUpdates) { #if PLATFORM(COCOA) m_activityStateChangeDispatcher->schedule(); #else dispatchActivityStateChange(); #endif } } void WebPageProxy::updateActivityState(ActivityState::Flags flagsToUpdate) { m_activityState &= ~flagsToUpdate; if (flagsToUpdate & ActivityState::IsFocused && m_pageClient.isViewFocused()) m_activityState |= ActivityState::IsFocused; if (flagsToUpdate & ActivityState::WindowIsActive && m_pageClient.isViewWindowActive()) m_activityState |= ActivityState::WindowIsActive; if (flagsToUpdate & ActivityState::IsVisible && m_pageClient.isViewVisible()) m_activityState |= ActivityState::IsVisible; if (flagsToUpdate & ActivityState::IsVisibleOrOccluded && m_pageClient.isViewVisibleOrOccluded()) m_activityState |= ActivityState::IsVisibleOrOccluded; if (flagsToUpdate & ActivityState::IsInWindow && m_pageClient.isViewInWindow()) m_activityState |= ActivityState::IsInWindow; if (flagsToUpdate & ActivityState::IsVisuallyIdle && m_pageClient.isVisuallyIdle()) m_activityState |= ActivityState::IsVisuallyIdle; if (flagsToUpdate & ActivityState::IsAudible && m_mediaState & MediaProducer::IsPlayingAudio && !(m_mutedState & MediaProducer::AudioIsMuted)) m_activityState |= ActivityState::IsAudible; if (flagsToUpdate & ActivityState::IsLoading && m_pageLoadState.isLoading()) m_activityState |= ActivityState::IsLoading; } void WebPageProxy::activityStateDidChange(ActivityState::Flags mayHaveChanged, bool wantsSynchronousReply, ActivityStateChangeDispatchMode dispatchMode) { m_potentiallyChangedActivityStateFlags |= mayHaveChanged; m_activityStateChangeWantsSynchronousReply = m_activityStateChangeWantsSynchronousReply || wantsSynchronousReply; if (m_suppressVisibilityUpdates && dispatchMode != ActivityStateChangeDispatchMode::Immediate) return; #if PLATFORM(COCOA) bool isNewlyInWindow = !isInWindow() && (mayHaveChanged & ActivityState::IsInWindow) && m_pageClient.isViewInWindow(); if (dispatchMode == ActivityStateChangeDispatchMode::Immediate || isNewlyInWindow) { dispatchActivityStateChange(); return; } m_activityStateChangeDispatcher->schedule(); #else UNUSED_PARAM(dispatchMode); dispatchActivityStateChange(); #endif } void WebPageProxy::viewDidLeaveWindow() { #if ENABLE(INPUT_TYPE_COLOR_POPOVER) && ENABLE(INPUT_TYPE_COLOR) // When leaving the current page, close the popover color well. if (m_colorPicker) endColorPicker(); #endif #if PLATFORM(IOS) && HAVE(AVKIT) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) // When leaving the current page, close the video fullscreen. if (m_videoFullscreenManager) m_videoFullscreenManager->requestHideAndExitFullscreen(); #endif } void WebPageProxy::viewDidEnterWindow() { LayerHostingMode layerHostingMode = m_pageClient.viewLayerHostingMode(); if (m_layerHostingMode != layerHostingMode) { m_layerHostingMode = layerHostingMode; m_process->send(Messages::WebPage::SetLayerHostingMode(layerHostingMode), m_pageID); } } void WebPageProxy::reloadAfterBeingKilledInBackground() { ASSERT(!isValid()); RELEASE_LOG_IF_ALLOWED("%p - Reloading tab that was killed in the background", this); // Only report as a crash if the page was ever visible, otherwise silently reload. if (m_hasEverBeenVisible) processDidCrash(); else reattachToWebProcessForReload(); } void WebPageProxy::dispatchActivityStateChange() { #if PLATFORM(COCOA) m_activityStateChangeDispatcher->invalidate(); #endif if (!isValid()) { if (m_potentiallyChangedActivityStateFlags & ActivityState::IsVisible && m_wasKilledForBeingUnresponsiveWhileInBackground) reloadAfterBeingKilledInBackground(); return; } // If the visibility state may have changed, then so may the visually idle & occluded agnostic state. if (m_potentiallyChangedActivityStateFlags & ActivityState::IsVisible) m_potentiallyChangedActivityStateFlags |= ActivityState::IsVisibleOrOccluded | ActivityState::IsVisuallyIdle; // Record the prior view state, update the flags that may have changed, // and check which flags have actually changed. ActivityState::Flags previousActivityState = m_activityState; updateActivityState(m_potentiallyChangedActivityStateFlags); ActivityState::Flags changed = m_activityState ^ previousActivityState; 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; if (changed || m_activityStateChangeWantsSynchronousReply || !m_nextActivityStateChangeCallbacks.isEmpty()) m_process->send(Messages::WebPage::SetActivityState(m_activityState, m_activityStateChangeWantsSynchronousReply, m_nextActivityStateChangeCallbacks), m_pageID); m_nextActivityStateChangeCallbacks.clear(); // 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) && !m_pageClient.isViewWindowActive()) || ((changed & ActivityState::IsFocused) && !(m_activityState & ActivityState::IsFocused))) requestPointerUnlock(); #endif if (changed & ActivityState::IsVisible) { if (isViewVisible()) { m_hasEverBeenVisible = true; 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->responsivenessTimer().stop(); } } if (changed & ActivityState::IsInWindow) { if (isInWindow()) viewDidEnterWindow(); else viewDidLeaveWindow(); } updateBackingStoreDiscardableState(); if (m_activityStateChangeWantsSynchronousReply) waitForDidUpdateActivityState(); m_potentiallyChangedActivityStateFlags = ActivityState::NoFlags; m_activityStateChangeWantsSynchronousReply = false; m_viewWasEverInWindow |= isNowInWindow; } bool WebPageProxy::isAlwaysOnLoggingAllowed() const { return sessionID().isAlwaysOnLoggingAllowed(); } 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) if (!isViewVisible() && !m_alwaysRunsAtForegroundPriority) { if (m_activityToken) { RELEASE_LOG_IF_ALLOWED("%p - UIProcess is releasing a foreground assertion because the view is no longer visible", this); m_activityToken = nullptr; } } else if (!m_activityToken) { if (isViewVisible()) RELEASE_LOG_IF_ALLOWED("%p - UIProcess is taking a foreground assertion because the view is visible", this); else RELEASE_LOG_IF_ALLOWED("%p - UIProcess is taking a foreground assertion even though the view is not visible because m_alwaysRunsAtForegroundPriority is true", this); m_activityToken = m_process->throttler().foregroundActivityToken(); } #endif } void WebPageProxy::updateHiddenPageThrottlingAutoIncreases() { if (!m_preferences->hiddenPageDOMTimerThrottlingAutoIncreases()) m_hiddenPageDOMTimerThrottlingAutoIncreasesCount = nullptr; else if (!m_hiddenPageDOMTimerThrottlingAutoIncreasesCount) m_hiddenPageDOMTimerThrottlingAutoIncreasesCount = m_process->processPool().hiddenPageThrottlingAutoIncreasesCount(); } void WebPageProxy::layerHostingModeDidChange() { if (!isValid()) return; LayerHostingMode layerHostingMode = m_pageClient.viewLayerHostingMode(); if (m_layerHostingMode == layerHostingMode) return; m_layerHostingMode = layerHostingMode; m_process->send(Messages::WebPage::SetLayerHostingMode(layerHostingMode), m_pageID); } void WebPageProxy::waitForDidUpdateActivityState() { if (!isValid()) 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) // 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_activityToken) { ASSERT_NOT_REACHED(); return; } #endif m_waitingForDidUpdateActivityState = true; m_drawingArea->waitForDidUpdateActivityState(); } IntSize WebPageProxy::viewSize() const { return m_pageClient.viewSize(); } void WebPageProxy::setInitialFocus(bool forward, bool isKeyboardEventValid, const WebKeyboardEvent& keyboardEvent, std::function callbackFunction) { if (!isValid()) { callbackFunction(CallbackBase::Error::OwnerWasInvalidated); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::SetInitialFocus(forward, isKeyboardEventValid, keyboardEvent, callbackID), m_pageID); } void WebPageProxy::clearSelection() { if (!isValid()) return; m_process->send(Messages::WebPage::ClearSelection(), m_pageID); } void WebPageProxy::restoreSelectionInFocusedEditableElement() { if (!isValid()) return; m_process->send(Messages::WebPage::RestoreSelectionInFocusedEditableElement(), m_pageID); } void WebPageProxy::validateCommand(const String& commandName, std::function callbackFunction) { if (!isValid()) { callbackFunction(String(), false, 0, CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::ValidateCommand(commandName, callbackID), m_pageID); } void WebPageProxy::setMaintainsInactiveSelection(bool newValue) { m_maintainsInactiveSelection = newValue; } void WebPageProxy::executeEditCommand(const String& commandName, const String& argument) { static NeverDestroyed ignoreSpellingCommandName(ASCIILiteral("ignoreSpelling")); if (!isValid()) return; if (commandName == ignoreSpellingCommandName) ++m_pendingLearnOrIgnoreWordMessageCount; m_process->send(Messages::WebPage::ExecuteEditCommand(commandName, argument), m_pageID); } void WebPageProxy::setEditable(bool editable) { if (editable == m_isEditable) return; if (!isValid()) return; m_isEditable = editable; m_process->send(Messages::WebPage::SetEditable(editable), m_pageID); } #if !PLATFORM(IOS) void WebPageProxy::didCommitLayerTree(const RemoteLayerTreeTransaction&) { } void WebPageProxy::layerTreeCommitComplete() { } #endif #if ENABLE(DRAG_SUPPORT) void WebPageProxy::dragEntered(DragData& dragData, const String& dragStorageName) { SandboxExtension::Handle sandboxExtensionHandle; SandboxExtension::HandleArray sandboxExtensionHandleEmptyArray; performDragControllerAction(DragControllerActionEntered, dragData, dragStorageName, sandboxExtensionHandle, sandboxExtensionHandleEmptyArray); } void WebPageProxy::dragUpdated(DragData& dragData, const String& dragStorageName) { SandboxExtension::Handle sandboxExtensionHandle; SandboxExtension::HandleArray sandboxExtensionHandleEmptyArray; performDragControllerAction(DragControllerActionUpdated, dragData, dragStorageName, sandboxExtensionHandle, sandboxExtensionHandleEmptyArray); } void WebPageProxy::dragExited(DragData& dragData, const String& dragStorageName) { SandboxExtension::Handle sandboxExtensionHandle; SandboxExtension::HandleArray sandboxExtensionHandleEmptyArray; performDragControllerAction(DragControllerActionExited, dragData, dragStorageName, sandboxExtensionHandle, sandboxExtensionHandleEmptyArray); } void WebPageProxy::performDragOperation(DragData& dragData, const String& dragStorageName, const SandboxExtension::Handle& sandboxExtensionHandle, const SandboxExtension::HandleArray& sandboxExtensionsForUpload) { performDragControllerAction(DragControllerActionPerformDragOperation, dragData, dragStorageName, sandboxExtensionHandle, sandboxExtensionsForUpload); } void WebPageProxy::performDragControllerAction(DragControllerAction action, DragData& dragData, const String& dragStorageName, const SandboxExtension::Handle& sandboxExtensionHandle, const SandboxExtension::HandleArray& sandboxExtensionsForUpload) { if (!isValid()) return; #if PLATFORM(GTK) UNUSED_PARAM(dragStorageName); UNUSED_PARAM(sandboxExtensionHandle); UNUSED_PARAM(sandboxExtensionsForUpload); String url = dragData.asURL(); if (!url.isEmpty()) m_process->assumeReadAccessToBaseURL(url); ASSERT(dragData.platformData()); WebSelectionData selection(*dragData.platformData()); m_process->send(Messages::WebPage::PerformDragControllerAction(action, dragData.clientPosition(), dragData.globalPosition(), dragData.draggingSourceOperationMask(), selection, dragData.flags()), m_pageID); #else m_process->send(Messages::WebPage::PerformDragControllerAction(action, dragData, sandboxExtensionHandle, sandboxExtensionsForUpload), m_pageID); #endif } void WebPageProxy::didPerformDragControllerAction(uint64_t dragOperation, bool mouseIsOverFileInput, unsigned numberOfItemsToBeAccepted) { MESSAGE_CHECK(dragOperation <= DragOperationDelete); m_currentDragOperation = static_cast(dragOperation); m_currentDragIsOverFileInput = mouseIsOverFileInput; m_currentDragNumberOfFilesToBeAccepted = numberOfItemsToBeAccepted; } #if PLATFORM(GTK) void WebPageProxy::startDrag(WebSelectionData&& selection, uint64_t dragOperation, const ShareableBitmap::Handle& dragImageHandle) { RefPtr dragImage = !dragImageHandle.isNull() ? ShareableBitmap::create(dragImageHandle) : nullptr; m_pageClient.startDrag(WTFMove(selection.selectionData), static_cast(dragOperation), WTFMove(dragImage)); m_process->send(Messages::WebPage::DidStartDrag(), m_pageID); } #endif void WebPageProxy::dragEnded(const IntPoint& clientPosition, const IntPoint& globalPosition, uint64_t operation) { if (!isValid()) return; m_process->send(Messages::WebPage::DragEnded(clientPosition, globalPosition, operation), m_pageID); } void WebPageProxy::dragCancelled() { if (isValid()) m_process->send(Messages::WebPage::DragCancelled(), m_pageID); } #endif // ENABLE(DRAG_SUPPORT) void WebPageProxy::handleMouseEvent(const NativeWebMouseEvent& event) { if (!isValid()) return; if (m_pageClient.windowIsFrontWindowUnderMouse(event)) setToolTip(String()); // NOTE: This does not start the responsiveness timer because mouse move should not indicate interaction. if (event.type() != WebEvent::MouseMove) m_process->responsivenessTimer().start(); else { if (m_processingMouseMoveEvent) { m_nextMouseMoveEvent = std::make_unique(event); return; } m_processingMouseMoveEvent = true; } // We need to keep track of the mouse down event in the case where we // display a popup menu for select elements. When the user changes the selected item, // we fake a mouse up event by using this stored down event. This event gets cleared // when the mouse up message is received from WebProcess. if (event.type() == WebEvent::MouseDown) m_currentlyProcessedMouseDownEvent = std::make_unique(event); m_process->send(Messages::WebPage::MouseEvent(event), m_pageID); } #if MERGE_WHEEL_EVENTS static bool canCoalesce(const WebWheelEvent& a, const WebWheelEvent& b) { if (a.position() != b.position()) return false; if (a.globalPosition() != b.globalPosition()) return false; if (a.modifiers() != b.modifiers()) return false; if (a.granularity() != b.granularity()) return false; #if PLATFORM(COCOA) if (a.phase() != b.phase()) return false; if (a.momentumPhase() != b.momentumPhase()) return false; if (a.hasPreciseScrollingDeltas() != b.hasPreciseScrollingDeltas()) return false; #endif return true; } static WebWheelEvent coalesce(const WebWheelEvent& a, const WebWheelEvent& b) { ASSERT(canCoalesce(a, b)); FloatSize mergedDelta = a.delta() + b.delta(); FloatSize mergedWheelTicks = a.wheelTicks() + b.wheelTicks(); #if PLATFORM(COCOA) FloatSize mergedUnacceleratedScrollingDelta = a.unacceleratedScrollingDelta() + b.unacceleratedScrollingDelta(); return WebWheelEvent(WebEvent::Wheel, b.position(), b.globalPosition(), mergedDelta, mergedWheelTicks, b.granularity(), b.directionInvertedFromDevice(), b.phase(), b.momentumPhase(), b.hasPreciseScrollingDeltas(), b.scrollCount(), mergedUnacceleratedScrollingDelta, b.modifiers(), b.timestamp()); #else return WebWheelEvent(WebEvent::Wheel, b.position(), b.globalPosition(), mergedDelta, mergedWheelTicks, b.granularity(), b.modifiers(), b.timestamp()); #endif } #endif // MERGE_WHEEL_EVENTS static WebWheelEvent coalescedWheelEvent(Deque& queue, Vector& coalescedEvents) { ASSERT(!queue.isEmpty()); ASSERT(coalescedEvents.isEmpty()); #if MERGE_WHEEL_EVENTS NativeWebWheelEvent firstEvent = queue.takeFirst(); coalescedEvents.append(firstEvent); WebWheelEvent event = firstEvent; while (!queue.isEmpty() && canCoalesce(event, queue.first())) { NativeWebWheelEvent firstEvent = queue.takeFirst(); coalescedEvents.append(firstEvent); event = coalesce(event, firstEvent); } return event; #else while (!queue.isEmpty()) coalescedEvents.append(queue.takeFirst()); return coalescedEvents.last(); #endif } void WebPageProxy::handleWheelEvent(const NativeWebWheelEvent& event) { #if ENABLE(ASYNC_SCROLLING) if (m_scrollingCoordinatorProxy && m_scrollingCoordinatorProxy->handleWheelEvent(platform(event))) return; #endif if (!isValid()) return; hideValidationMessage(); if (!m_currentlyProcessedWheelEvents.isEmpty()) { m_wheelEventQueue.append(event); if (m_wheelEventQueue.size() < wheelEventQueueSizeThreshold) return; // The queue has too many wheel events, so push a new event. } if (!m_wheelEventQueue.isEmpty()) { processNextQueuedWheelEvent(); return; } auto coalescedWheelEvent = std::make_unique>(); coalescedWheelEvent->append(event); m_currentlyProcessedWheelEvents.append(WTFMove(coalescedWheelEvent)); sendWheelEvent(event); } void WebPageProxy::processNextQueuedWheelEvent() { auto nextCoalescedEvent = std::make_unique>(); WebWheelEvent nextWheelEvent = coalescedWheelEvent(m_wheelEventQueue, *nextCoalescedEvent.get()); m_currentlyProcessedWheelEvents.append(WTFMove(nextCoalescedEvent)); sendWheelEvent(nextWheelEvent); } void WebPageProxy::sendWheelEvent(const WebWheelEvent& event) { m_process->send( Messages::EventDispatcher::WheelEvent( m_pageID, event, shouldUseImplicitRubberBandControl() ? !m_backForwardList->backItem() : rubberBandsAtLeft(), shouldUseImplicitRubberBandControl() ? !m_backForwardList->forwardItem() : rubberBandsAtRight(), rubberBandsAtTop(), rubberBandsAtBottom() ), 0); // Manually ping the web process to check for responsiveness since our wheel // event will dispatch to a non-main thread, which always responds. m_process->isResponsive(nullptr); } void WebPageProxy::handleKeyboardEvent(const NativeWebKeyboardEvent& event) { if (!isValid()) return; LOG(KeyHandling, "WebPageProxy::handleKeyboardEvent: %s", webKeyboardEventTypeString(event.type())); m_keyEventQueue.append(event); m_process->responsivenessTimer().start(); if (m_keyEventQueue.size() == 1) { // Otherwise, sent from DidReceiveEvent message handler. LOG(KeyHandling, " UI process: sent keyEvent from handleKeyboardEvent"); m_process->send(Messages::WebPage::KeyEvent(event), m_pageID); } } WebPreferencesStore WebPageProxy::preferencesStore() const { if (m_configurationPreferenceValues.isEmpty()) return m_preferences->store(); WebPreferencesStore store = m_preferences->store(); for (const auto& preference : m_configurationPreferenceValues) store.m_values.set(preference.key, preference.value); return store; } #if ENABLE(NETSCAPE_PLUGIN_API) void WebPageProxy::findPlugin(const String& mimeType, uint32_t processType, const String& urlString, const String& frameURLString, const String& pageURLString, bool allowOnlyApplicationPlugins, uint64_t& pluginProcessToken, String& newMimeType, uint32_t& pluginLoadPolicy, String& unavailabilityDescription) { PageClientProtector protector(m_pageClient); MESSAGE_CHECK_URL(urlString); newMimeType = mimeType.convertToASCIILowercase(); pluginLoadPolicy = PluginModuleLoadNormally; PluginData::AllowedPluginTypes allowedPluginTypes = allowOnlyApplicationPlugins ? PluginData::OnlyApplicationPlugins : PluginData::AllPlugins; PluginModuleInfo plugin = m_process->processPool().pluginInfoStore().findPlugin(newMimeType, URL(URL(), urlString), allowedPluginTypes); if (!plugin.path) { pluginProcessToken = 0; return; } pluginLoadPolicy = PluginInfoStore::defaultLoadPolicyForPlugin(plugin); #if PLATFORM(COCOA) RefPtr pluginInformation = createPluginInformationDictionary(plugin, frameURLString, String(), pageURLString, String(), String()); if (m_navigationClient) pluginLoadPolicy = m_navigationClient->decidePolicyForPluginLoad(*this, static_cast(pluginLoadPolicy), pluginInformation.get(), unavailabilityDescription); else pluginLoadPolicy = m_loaderClient->pluginLoadPolicy(*this, static_cast(pluginLoadPolicy), pluginInformation.get(), unavailabilityDescription); #else UNUSED_PARAM(frameURLString); UNUSED_PARAM(pageURLString); UNUSED_PARAM(unavailabilityDescription); #endif PluginProcessSandboxPolicy pluginProcessSandboxPolicy = PluginProcessSandboxPolicyNormal; switch (pluginLoadPolicy) { case PluginModuleLoadNormally: pluginProcessSandboxPolicy = PluginProcessSandboxPolicyNormal; break; case PluginModuleLoadUnsandboxed: pluginProcessSandboxPolicy = PluginProcessSandboxPolicyUnsandboxed; break; case PluginModuleBlockedForSecurity: case PluginModuleBlockedForCompatibility: pluginProcessToken = 0; return; } pluginProcessToken = PluginProcessManager::singleton().pluginProcessToken(plugin, static_cast(processType), pluginProcessSandboxPolicy); } #endif // ENABLE(NETSCAPE_PLUGIN_API) #if ENABLE(TOUCH_EVENTS) static TrackingType mergeTrackingTypes(TrackingType a, TrackingType b) { if (static_cast(b) > static_cast(a)) return b; return a; } void WebPageProxy::updateTouchEventTracking(const WebTouchEvent& touchStartEvent) { #if ENABLE(ASYNC_SCROLLING) const EventNames& names = eventNames(); for (auto& touchPoint : touchStartEvent.touchPoints()) { IntPoint location = touchPoint.location(); auto updateTrackingType = [this, location](TrackingType& trackingType, const AtomicString& eventName) { if (trackingType == TrackingType::Synchronous) return; TrackingType trackingTypeForLocation = m_scrollingCoordinatorProxy->eventTrackingTypeForPoint(eventName, location); trackingType = mergeTrackingTypes(trackingType, trackingTypeForLocation); }; updateTrackingType(m_touchEventTracking.touchForceChangedTracking, names.touchforcechangeEvent); updateTrackingType(m_touchEventTracking.touchStartTracking, names.touchstartEvent); updateTrackingType(m_touchEventTracking.touchMoveTracking, names.touchmoveEvent); updateTrackingType(m_touchEventTracking.touchEndTracking, names.touchendEvent); } #else UNUSED_PARAM(touchStartEvent); m_touchEventTracking.touchForceChangedTracking = TrackingType::Synchronous; m_touchEventTracking.touchStartTracking = TrackingType::Synchronous; m_touchEventTracking.touchMoveTracking = TrackingType::Synchronous; m_touchEventTracking.touchEndTracking = TrackingType::Synchronous; #endif // ENABLE(ASYNC_SCROLLING) } TrackingType WebPageProxy::touchEventTrackingType(const WebTouchEvent& touchStartEvent) const { // We send all events if any type is needed, we just do it asynchronously for the types that are not tracked. // // Touch events define a sequence with strong dependencies. For example, we can expect // a TouchMove to only appear after a TouchStart, and the ids of the touch points is consistent between // the two. // // WebCore should not have to set up its state correctly after some events were dismissed. // For example, we don't want to send a TouchMoved without a TouchPressed. // We send everything, WebCore updates its internal state and dispatch what is needed to the page. TrackingType globalTrackingType = m_touchEventTracking.isTrackingAnything() ? TrackingType::Asynchronous : TrackingType::NotTracking; globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchEventTracking.touchForceChangedTracking); for (auto& touchPoint : touchStartEvent.touchPoints()) { switch (touchPoint.state()) { case WebPlatformTouchPoint::TouchReleased: globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchEventTracking.touchEndTracking); break; case WebPlatformTouchPoint::TouchPressed: globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchEventTracking.touchStartTracking); break; case WebPlatformTouchPoint::TouchMoved: case WebPlatformTouchPoint::TouchStationary: globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchEventTracking.touchMoveTracking); break; case WebPlatformTouchPoint::TouchCancelled: globalTrackingType = mergeTrackingTypes(globalTrackingType, TrackingType::Asynchronous); break; } } return globalTrackingType; } #endif #if ENABLE(MAC_GESTURE_EVENTS) void WebPageProxy::handleGestureEvent(const NativeWebGestureEvent& event) { if (!isValid()) return; m_gestureEventQueue.append(event); // FIXME: Consider doing some coalescing here. m_process->responsivenessTimer().start(); m_process->send(Messages::EventDispatcher::GestureEvent(m_pageID, event), 0); } #endif #if ENABLE(IOS_TOUCH_EVENTS) void WebPageProxy::handleTouchEventSynchronously(NativeWebTouchEvent& event) { if (!isValid()) return; if (event.type() == WebEvent::TouchStart) { updateTouchEventTracking(event); m_layerTreeTransactionIdAtLastTouchStart = downcast(*drawingArea()).lastCommittedLayerTreeTransactionID(); } TrackingType touchEventsTrackingType = touchEventTrackingType(event); if (touchEventsTrackingType == TrackingType::NotTracking) return; if (touchEventsTrackingType == TrackingType::Asynchronous) { // We can end up here if a native gesture has not started but the event handlers are passive. // // The client of WebPageProxy asks the event to be sent synchronously since the touch event // can prevent a native gesture. // But, here we know that all events handlers that can handle this events are passive. // We can use asynchronous dispatch and pretend to the client that the page does nothing with the events. event.setCanPreventNativeGestures(false); handleTouchEventAsynchronously(event); didReceiveEvent(event.type(), false); return; } m_process->responsivenessTimer().start(); bool handled = false; m_process->sendSync(Messages::WebPage::TouchEventSync(event), Messages::WebPage::TouchEventSync::Reply(handled), m_pageID); didReceiveEvent(event.type(), handled); m_pageClient.doneWithTouchEvent(event, handled); m_process->responsivenessTimer().stop(); if (event.allTouchPointsAreReleased()) m_touchEventTracking.reset(); } void WebPageProxy::handleTouchEventAsynchronously(const NativeWebTouchEvent& event) { if (!isValid()) return; TrackingType touchEventsTrackingType = touchEventTrackingType(event); if (touchEventsTrackingType == TrackingType::NotTracking) return; m_process->send(Messages::EventDispatcher::TouchEvent(m_pageID, event), 0); if (event.allTouchPointsAreReleased()) m_touchEventTracking.reset(); } #elif ENABLE(TOUCH_EVENTS) void WebPageProxy::handleTouchEvent(const NativeWebTouchEvent& event) { if (!isValid()) return; if (event.type() == WebEvent::TouchStart) updateTouchEventTracking(event); if (touchEventTrackingType(event) == TrackingType::NotTracking) return; // If the page is suspended, which should be the case during panning, pinching // and animation on the page itself (kinetic scrolling, tap to zoom) etc, then // we do not send any of the events to the page even if is has listeners. if (!m_isPageSuspended) { m_touchEventQueue.append(event); m_process->responsivenessTimer().start(); m_process->send(Messages::WebPage::TouchEvent(event), m_pageID); } else { if (m_touchEventQueue.isEmpty()) { bool isEventHandled = false; m_pageClient.doneWithTouchEvent(event, isEventHandled); } else { // We attach the incoming events to the newest queued event so that all // the events are delivered in the correct order when the event is dequed. QueuedTouchEvents& lastEvent = m_touchEventQueue.last(); lastEvent.deferredTouchEvents.append(event); } } if (event.allTouchPointsAreReleased()) m_touchEventTracking.reset(); } #endif // ENABLE(TOUCH_EVENTS) void WebPageProxy::scrollBy(ScrollDirection direction, ScrollGranularity granularity) { if (!isValid()) return; m_process->send(Messages::WebPage::ScrollBy(direction, granularity), m_pageID); } void WebPageProxy::centerSelectionInVisibleArea() { if (!isValid()) return; m_process->send(Messages::WebPage::CenterSelectionInVisibleArea(), m_pageID); } void WebPageProxy::receivedPolicyDecision(PolicyAction action, WebFrameProxy& frame, uint64_t listenerID, API::Navigation* navigation, const WebsitePolicies& websitePolicies) { if (!isValid()) return; auto transaction = m_pageLoadState.transaction(); if (action == PolicyIgnore) m_pageLoadState.clearPendingAPIRequestURL(transaction); #if ENABLE(DOWNLOAD_ATTRIBUTE) if (m_syncNavigationActionHasDownloadAttribute && action == PolicyUse) action = PolicyDownload; #endif DownloadID downloadID = { }; if (action == PolicyDownload) { // Create a download proxy. // FIXME: We should ensure that the downloadRequest is never empty. const ResourceRequest& downloadRequest = m_decidePolicyForResponseRequest ? *m_decidePolicyForResponseRequest : ResourceRequest(); DownloadProxy* download = m_process->processPool().createDownloadProxy(downloadRequest); downloadID = download->downloadID(); handleDownloadRequest(download); } // If we received a policy decision while in decidePolicyForResponse the decision will // be sent back to the web process by decidePolicyForResponse. if (m_inDecidePolicyForResponseSync) { m_syncMimeTypePolicyActionIsValid = true; m_syncMimeTypePolicyAction = action; m_syncMimeTypePolicyDownloadID = downloadID; return; } // If we received a policy decision while in decidePolicyForNavigationAction the decision will // be sent back to the web process by decidePolicyForNavigationAction. if (m_inDecidePolicyForNavigationAction) { m_syncNavigationActionPolicyActionIsValid = true; m_syncNavigationActionPolicyAction = action; m_syncNavigationActionPolicyDownloadID = downloadID; m_syncNavigationActionPolicyWebsitePolicies = websitePolicies; return; } m_process->send(Messages::WebPage::DidReceivePolicyDecision(frame.frameID(), listenerID, action, navigation ? navigation->navigationID() : 0, downloadID), m_pageID); } void WebPageProxy::setUserAgent(const String& userAgent) { if (m_userAgent == userAgent) return; m_userAgent = userAgent; if (!isValid()) return; m_process->send(Messages::WebPage::SetUserAgent(m_userAgent), m_pageID); } void WebPageProxy::setApplicationNameForUserAgent(const String& applicationName) { if (m_applicationNameForUserAgent == applicationName) return; m_applicationNameForUserAgent = applicationName; if (!m_customUserAgent.isEmpty()) return; setUserAgent(standardUserAgent(m_applicationNameForUserAgent)); } void WebPageProxy::setCustomUserAgent(const String& customUserAgent) { if (m_customUserAgent == customUserAgent) return; m_customUserAgent = customUserAgent; if (m_customUserAgent.isEmpty()) { setUserAgent(standardUserAgent(m_applicationNameForUserAgent)); return; } setUserAgent(m_customUserAgent); } void WebPageProxy::resumeActiveDOMObjectsAndAnimations() { if (!isValid() || !m_isPageSuspended) return; m_isPageSuspended = false; m_process->send(Messages::WebPage::ResumeActiveDOMObjectsAndAnimations(), m_pageID); } void WebPageProxy::suspendActiveDOMObjectsAndAnimations() { if (!isValid() || m_isPageSuspended) return; m_isPageSuspended = true; m_process->send(Messages::WebPage::SuspendActiveDOMObjectsAndAnimations(), m_pageID); } bool WebPageProxy::supportsTextEncoding() const { // FIXME (118840): We should probably only support this for text documents, not all non-image documents. return m_mainFrame && !m_mainFrame->isDisplayingStandaloneImageDocument(); } void WebPageProxy::setCustomTextEncodingName(const String& encodingName) { if (m_customTextEncodingName == encodingName) return; m_customTextEncodingName = encodingName; if (!isValid()) return; m_process->send(Messages::WebPage::SetCustomTextEncodingName(encodingName), m_pageID); } void WebPageProxy::terminateProcess(TerminationReason terminationReason) { // NOTE: This uses a check of m_isValid rather than calling isValid() since // we want this to run even for pages being closed or that already closed. if (!m_isValid) return; m_process->requestTermination(); resetStateAfterProcessExited(); m_wasKilledForBeingUnresponsiveWhileInBackground = terminationReason == TerminationReason::UnresponsiveWhileInBackground; } SessionState WebPageProxy::sessionState(const std::function& filter) const { SessionState sessionState; sessionState.backForwardListState = m_backForwardList->backForwardListState(filter); String provisionalURLString = m_pageLoadState.pendingAPIRequestURL(); if (provisionalURLString.isEmpty()) provisionalURLString = m_pageLoadState.provisionalURL(); if (!provisionalURLString.isEmpty()) sessionState.provisionalURL = URL(URL(), provisionalURLString); sessionState.renderTreeSize = renderTreeSize(); return sessionState; } RefPtr WebPageProxy::restoreFromSessionState(SessionState sessionState, bool navigate) { m_sessionRestorationRenderTreeSize = 0; m_hitRenderTreeSizeThreshold = false; bool hasBackForwardList = !!sessionState.backForwardListState.currentIndex; if (hasBackForwardList) { m_backForwardList->restoreFromState(WTFMove(sessionState.backForwardListState)); for (const auto& entry : m_backForwardList->entries()) process().registerNewWebBackForwardListItem(entry.get()); process().send(Messages::WebPage::RestoreSession(m_backForwardList->itemStates()), m_pageID); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setCanGoBack(transaction, m_backForwardList->backItem()); m_pageLoadState.setCanGoForward(transaction, m_backForwardList->forwardItem()); // The back / forward list was restored from a sessionState so we don't want to snapshot the current // page when navigating away. Suppress navigation snapshotting until the next load has committed m_suppressAutomaticNavigationSnapshotting = true; } // FIXME: Navigating should be separate from state restoration. if (navigate) { m_sessionRestorationRenderTreeSize = sessionState.renderTreeSize; if (!m_sessionRestorationRenderTreeSize) m_hitRenderTreeSizeThreshold = true; // If we didn't get data on renderTreeSize, just don't fire the milestone. if (!sessionState.provisionalURL.isNull()) return loadRequest(sessionState.provisionalURL); if (hasBackForwardList) { // FIXME: Do we have to null check the back forward list item here? if (WebBackForwardListItem* item = m_backForwardList->currentItem()) return goToBackForwardItem(item); } } return nullptr; } bool WebPageProxy::supportsTextZoom() const { // FIXME (118840): This should also return false for standalone media and plug-in documents. if (!m_mainFrame || m_mainFrame->isDisplayingStandaloneImageDocument()) return false; return true; } void WebPageProxy::setTextZoomFactor(double zoomFactor) { if (!isValid()) return; if (m_textZoomFactor == zoomFactor) return; m_textZoomFactor = zoomFactor; m_process->send(Messages::WebPage::SetTextZoomFactor(m_textZoomFactor), m_pageID); } void WebPageProxy::setPageZoomFactor(double zoomFactor) { if (!isValid()) return; if (m_pageZoomFactor == zoomFactor) return; hideValidationMessage(); m_pageZoomFactor = zoomFactor; m_process->send(Messages::WebPage::SetPageZoomFactor(m_pageZoomFactor), m_pageID); } void WebPageProxy::setPageAndTextZoomFactors(double pageZoomFactor, double textZoomFactor) { if (!isValid()) return; if (m_pageZoomFactor == pageZoomFactor && m_textZoomFactor == textZoomFactor) return; hideValidationMessage(); m_pageZoomFactor = pageZoomFactor; m_textZoomFactor = textZoomFactor; m_process->send(Messages::WebPage::SetPageAndTextZoomFactors(m_pageZoomFactor, m_textZoomFactor), m_pageID); } double WebPageProxy::pageZoomFactor() const { // Zoom factor for non-PDF pages persists across page loads. We maintain a separate member variable for PDF // zoom which ensures that we don't use the PDF zoom for a normal page. if (m_mainFramePluginHandlesPageScaleGesture) return m_pluginZoomFactor; return m_pageZoomFactor; } double WebPageProxy::pageScaleFactor() const { // PDF documents use zoom and scale factors to size themselves appropriately in the window. We store them // separately but decide which to return based on the main frame. if (m_mainFramePluginHandlesPageScaleGesture) return m_pluginScaleFactor; return m_pageScaleFactor; } void WebPageProxy::scalePage(double scale, const IntPoint& origin) { ASSERT(scale > 0); if (!isValid()) return; m_pageScaleFactor = scale; m_process->send(Messages::WebPage::ScalePage(scale, origin), m_pageID); } void WebPageProxy::scalePageInViewCoordinates(double scale, const IntPoint& centerInViewCoordinates) { ASSERT(scale > 0); if (!isValid()) return; m_pageScaleFactor = scale; m_process->send(Messages::WebPage::ScalePageInViewCoordinates(scale, centerInViewCoordinates), m_pageID); } void WebPageProxy::scaleView(double scale) { ASSERT(scale > 0); if (!isValid()) return; m_viewScaleFactor = scale; m_process->send(Messages::WebPage::ScaleView(scale), m_pageID); } void WebPageProxy::setIntrinsicDeviceScaleFactor(float scaleFactor) { if (m_intrinsicDeviceScaleFactor == scaleFactor) return; m_intrinsicDeviceScaleFactor = scaleFactor; if (m_drawingArea) m_drawingArea->deviceScaleFactorDidChange(); } void WebPageProxy::windowScreenDidChange(PlatformDisplayID displayID) { if (!isValid()) return; m_process->send(Messages::WebPage::WindowScreenDidChange(displayID), m_pageID); } float WebPageProxy::deviceScaleFactor() const { if (m_customDeviceScaleFactor) return m_customDeviceScaleFactor; return m_intrinsicDeviceScaleFactor; } void WebPageProxy::setCustomDeviceScaleFactor(float customScaleFactor) { if (!isValid()) return; // FIXME: Remove this once we bump cairo requirements to support HiDPI. // https://bugs.webkit.org/show_bug.cgi?id=133378 #if USE(CAIRO) && !HAVE(CAIRO_SURFACE_SET_DEVICE_SCALE) return; #endif if (m_customDeviceScaleFactor == customScaleFactor) return; float oldScaleFactor = deviceScaleFactor(); m_customDeviceScaleFactor = customScaleFactor; if (deviceScaleFactor() != oldScaleFactor) m_drawingArea->deviceScaleFactorDidChange(); } void WebPageProxy::accessibilitySettingsDidChange() { if (!isValid()) return; m_process->send(Messages::WebPage::AccessibilitySettingsDidChange(), m_pageID); } void WebPageProxy::setUseFixedLayout(bool fixed) { if (!isValid()) return; // This check is fine as the value is initialized in the web // process as part of the creation parameters. if (fixed == m_useFixedLayout) return; m_useFixedLayout = fixed; if (!fixed) m_fixedLayoutSize = IntSize(); m_process->send(Messages::WebPage::SetUseFixedLayout(fixed), m_pageID); } void WebPageProxy::setFixedLayoutSize(const IntSize& size) { if (!isValid()) return; if (size == m_fixedLayoutSize) return; m_fixedLayoutSize = size; m_process->send(Messages::WebPage::SetFixedLayoutSize(size), m_pageID); } void WebPageProxy::listenForLayoutMilestones(WebCore::LayoutMilestones milestones) { if (!isValid()) return; if (milestones == m_observedLayoutMilestones) return; m_observedLayoutMilestones = milestones; m_process->send(Messages::WebPage::ListenForLayoutMilestones(milestones), m_pageID); } void WebPageProxy::setSuppressScrollbarAnimations(bool suppressAnimations) { if (!isValid()) return; if (suppressAnimations == m_suppressScrollbarAnimations) return; m_suppressScrollbarAnimations = suppressAnimations; m_process->send(Messages::WebPage::SetSuppressScrollbarAnimations(suppressAnimations), m_pageID); } bool WebPageProxy::rubberBandsAtLeft() const { return m_rubberBandsAtLeft; } void WebPageProxy::setRubberBandsAtLeft(bool rubberBandsAtLeft) { m_rubberBandsAtLeft = rubberBandsAtLeft; } bool WebPageProxy::rubberBandsAtRight() const { return m_rubberBandsAtRight; } void WebPageProxy::setRubberBandsAtRight(bool rubberBandsAtRight) { m_rubberBandsAtRight = rubberBandsAtRight; } bool WebPageProxy::rubberBandsAtTop() const { return m_rubberBandsAtTop; } void WebPageProxy::setRubberBandsAtTop(bool rubberBandsAtTop) { m_rubberBandsAtTop = rubberBandsAtTop; } bool WebPageProxy::rubberBandsAtBottom() const { return m_rubberBandsAtBottom; } void WebPageProxy::setRubberBandsAtBottom(bool rubberBandsAtBottom) { m_rubberBandsAtBottom = rubberBandsAtBottom; } void WebPageProxy::setEnableVerticalRubberBanding(bool enableVerticalRubberBanding) { if (enableVerticalRubberBanding == m_enableVerticalRubberBanding) return; m_enableVerticalRubberBanding = enableVerticalRubberBanding; if (!isValid()) return; m_process->send(Messages::WebPage::SetEnableVerticalRubberBanding(enableVerticalRubberBanding), m_pageID); } bool WebPageProxy::verticalRubberBandingIsEnabled() const { return m_enableVerticalRubberBanding; } void WebPageProxy::setEnableHorizontalRubberBanding(bool enableHorizontalRubberBanding) { if (enableHorizontalRubberBanding == m_enableHorizontalRubberBanding) return; m_enableHorizontalRubberBanding = enableHorizontalRubberBanding; if (!isValid()) return; m_process->send(Messages::WebPage::SetEnableHorizontalRubberBanding(enableHorizontalRubberBanding), m_pageID); } bool WebPageProxy::horizontalRubberBandingIsEnabled() const { return m_enableHorizontalRubberBanding; } void WebPageProxy::setBackgroundExtendsBeyondPage(bool backgroundExtendsBeyondPage) { if (backgroundExtendsBeyondPage == m_backgroundExtendsBeyondPage) return; m_backgroundExtendsBeyondPage = backgroundExtendsBeyondPage; if (!isValid()) return; m_process->send(Messages::WebPage::SetBackgroundExtendsBeyondPage(backgroundExtendsBeyondPage), m_pageID); } bool WebPageProxy::backgroundExtendsBeyondPage() const { return m_backgroundExtendsBeyondPage; } void WebPageProxy::setPaginationMode(WebCore::Pagination::Mode mode) { if (mode == m_paginationMode) return; m_paginationMode = mode; if (!isValid()) return; m_process->send(Messages::WebPage::SetPaginationMode(mode), m_pageID); } void WebPageProxy::setPaginationBehavesLikeColumns(bool behavesLikeColumns) { if (behavesLikeColumns == m_paginationBehavesLikeColumns) return; m_paginationBehavesLikeColumns = behavesLikeColumns; if (!isValid()) return; m_process->send(Messages::WebPage::SetPaginationBehavesLikeColumns(behavesLikeColumns), m_pageID); } void WebPageProxy::setPageLength(double pageLength) { if (pageLength == m_pageLength) return; m_pageLength = pageLength; if (!isValid()) return; m_process->send(Messages::WebPage::SetPageLength(pageLength), m_pageID); } void WebPageProxy::setGapBetweenPages(double gap) { if (gap == m_gapBetweenPages) return; m_gapBetweenPages = gap; if (!isValid()) return; m_process->send(Messages::WebPage::SetGapBetweenPages(gap), m_pageID); } void WebPageProxy::setPaginationLineGridEnabled(bool lineGridEnabled) { if (lineGridEnabled == m_paginationLineGridEnabled) return; m_paginationLineGridEnabled = lineGridEnabled; if (!isValid()) return; m_process->send(Messages::WebPage::SetPaginationLineGridEnabled(lineGridEnabled), m_pageID); } void WebPageProxy::pageScaleFactorDidChange(double scaleFactor) { m_pageScaleFactor = scaleFactor; } void WebPageProxy::pluginScaleFactorDidChange(double pluginScaleFactor) { m_pluginScaleFactor = pluginScaleFactor; } void WebPageProxy::pluginZoomFactorDidChange(double pluginZoomFactor) { m_pluginZoomFactor = pluginZoomFactor; } void WebPageProxy::findStringMatches(const String& string, FindOptions options, unsigned maxMatchCount) { if (string.isEmpty()) { didFindStringMatches(string, Vector> (), 0); return; } m_process->send(Messages::WebPage::FindStringMatches(string, options, maxMatchCount), m_pageID); } void WebPageProxy::findString(const String& string, FindOptions options, unsigned maxMatchCount) { m_process->send(Messages::WebPage::FindString(string, options, maxMatchCount), m_pageID); } void WebPageProxy::getImageForFindMatch(int32_t matchIndex) { m_process->send(Messages::WebPage::GetImageForFindMatch(matchIndex), m_pageID); } void WebPageProxy::selectFindMatch(int32_t matchIndex) { m_process->send(Messages::WebPage::SelectFindMatch(matchIndex), m_pageID); } void WebPageProxy::hideFindUI() { m_process->send(Messages::WebPage::HideFindUI(), m_pageID); } void WebPageProxy::countStringMatches(const String& string, FindOptions options, unsigned maxMatchCount) { if (!isValid()) return; m_process->send(Messages::WebPage::CountStringMatches(string, options, maxMatchCount), m_pageID); } void WebPageProxy::runJavaScriptInMainFrame(const String& script, std::function callbackFunction) { if (!isValid()) { callbackFunction(nullptr, false, { }, CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::RunJavaScriptInMainFrame(script, callbackID), m_pageID); } void WebPageProxy::getRenderTreeExternalRepresentation(std::function callbackFunction) { if (!isValid()) { callbackFunction(String(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::GetRenderTreeExternalRepresentation(callbackID), m_pageID); } void WebPageProxy::getSourceForFrame(WebFrameProxy* frame, std::function callbackFunction) { if (!isValid()) { callbackFunction(String(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_loadDependentStringCallbackIDs.add(callbackID); m_process->send(Messages::WebPage::GetSourceForFrame(frame->frameID(), callbackID), m_pageID); } void WebPageProxy::getContentsAsString(std::function callbackFunction) { if (!isValid()) { callbackFunction(String(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_loadDependentStringCallbackIDs.add(callbackID); m_process->send(Messages::WebPage::GetContentsAsString(callbackID), m_pageID); } void WebPageProxy::getBytecodeProfile(std::function callbackFunction) { if (!isValid()) { callbackFunction(String(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_loadDependentStringCallbackIDs.add(callbackID); m_process->send(Messages::WebPage::GetBytecodeProfile(callbackID), m_pageID); } void WebPageProxy::getSamplingProfilerOutput(std::function callbackFunction) { if (!isValid()) { callbackFunction(String(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_loadDependentStringCallbackIDs.add(callbackID); m_process->send(Messages::WebPage::GetSamplingProfilerOutput(callbackID), m_pageID); } void WebPageProxy::isWebProcessResponsive(std::function callbackFunction) { if (!isValid()) { RunLoop::main().dispatch([callbackFunction = WTFMove(callbackFunction)] { bool isWebProcessResponsive = true; callbackFunction(isWebProcessResponsive); }); return; } m_process->isResponsive(callbackFunction); } #if ENABLE(MHTML) void WebPageProxy::getContentsAsMHTMLData(std::function callbackFunction) { if (!isValid()) { callbackFunction(nullptr, CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::GetContentsAsMHTMLData(callbackID), m_pageID); } #endif void WebPageProxy::getSelectionOrContentsAsString(std::function callbackFunction) { if (!isValid()) { callbackFunction(String(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::GetSelectionOrContentsAsString(callbackID), m_pageID); } void WebPageProxy::getSelectionAsWebArchiveData(std::function callbackFunction) { if (!isValid()) { callbackFunction(nullptr, CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::GetSelectionAsWebArchiveData(callbackID), m_pageID); } void WebPageProxy::getMainResourceDataOfFrame(WebFrameProxy* frame, std::function callbackFunction) { if (!isValid() || !frame) { callbackFunction(nullptr, CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::GetMainResourceDataOfFrame(frame->frameID(), callbackID), m_pageID); } void WebPageProxy::getResourceDataFromFrame(WebFrameProxy* frame, API::URL* resourceURL, std::function callbackFunction) { if (!isValid()) { callbackFunction(nullptr, CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::GetResourceDataFromFrame(frame->frameID(), resourceURL->string(), callbackID), m_pageID); } void WebPageProxy::getWebArchiveOfFrame(WebFrameProxy* frame, std::function callbackFunction) { if (!isValid()) { callbackFunction(nullptr, CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::GetWebArchiveOfFrame(frame->frameID(), callbackID), m_pageID); } void WebPageProxy::forceRepaint(PassRefPtr prpCallback) { RefPtr callback = prpCallback; if (!isValid()) { // FIXME: If the page is invalid we should not call the callback. It'd be better to just return false from forceRepaint. callback->invalidate(CallbackBase::Error::OwnerWasInvalidated); return; } std::function didForceRepaintCallback = [this, callback](CallbackBase::Error error) { if (error != CallbackBase::Error::None) { callback->invalidate(error); return; } if (!isValid()) { callback->invalidate(CallbackBase::Error::OwnerWasInvalidated); return; } callAfterNextPresentationUpdate([callback](CallbackBase::Error error) { if (error != CallbackBase::Error::None) { callback->invalidate(error); return; } callback->performCallback(); }); }; uint64_t callbackID = m_callbacks.put(didForceRepaintCallback, m_process->throttler().backgroundActivityToken()); m_drawingArea->waitForBackingStoreUpdateOnNextPaint(); m_process->send(Messages::WebPage::ForceRepaint(callbackID), m_pageID); } static OptionSet printingSendOptions(bool isPerformingDOMPrintOperation) { if (isPerformingDOMPrintOperation) return IPC::SendOption::DispatchMessageEvenWhenWaitingForSyncReply; return { }; } void WebPageProxy::preferencesDidChange() { if (!isValid()) return; #if ENABLE(INSPECTOR_SERVER) if (m_preferences->developerExtrasEnabled()) inspector()->enableRemoteInspection(); #endif updateThrottleState(); updateHiddenPageThrottlingAutoIncreases(); m_pageClient.preferencesDidChange(); // FIXME: It probably makes more sense to send individual preference changes. // However, WebKitTestRunner depends on getting a preference change notification // even if nothing changed in UI process, so that overrides get removed. // Preferences need to be updated during synchronous printing to make "print backgrounds" preference work when toggled from a print dialog checkbox. m_process->send(Messages::WebPage::PreferencesDidChange(preferencesStore()), m_pageID, printingSendOptions(m_isPerformingDOMPrintOperation)); } void WebPageProxy::didCreateMainFrame(uint64_t frameID) { PageClientProtector protector(m_pageClient); MESSAGE_CHECK(!m_mainFrame); MESSAGE_CHECK(m_process->canCreateFrame(frameID)); m_mainFrame = WebFrameProxy::create(this, frameID); // Add the frame to the process wide map. m_process->frameCreated(frameID, m_mainFrame.get()); } void WebPageProxy::didCreateSubframe(uint64_t frameID) { PageClientProtector protector(m_pageClient); MESSAGE_CHECK(m_mainFrame); MESSAGE_CHECK(m_process->canCreateFrame(frameID)); RefPtr subFrame = WebFrameProxy::create(this, frameID); // Add the frame to the process wide map. m_process->frameCreated(frameID, subFrame.get()); } double WebPageProxy::estimatedProgress() const { return m_pageLoadState.estimatedProgress(); } void WebPageProxy::didStartProgress() { PageClientProtector protector(m_pageClient); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.didStartProgress(transaction); m_pageLoadState.commitChanges(); m_loaderClient->didStartProgress(*this); } void WebPageProxy::didChangeProgress(double value) { PageClientProtector protector(m_pageClient); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.didChangeProgress(transaction, value); m_pageLoadState.commitChanges(); m_loaderClient->didChangeProgress(*this); } void WebPageProxy::didFinishProgress() { PageClientProtector protector(m_pageClient); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.didFinishProgress(transaction); m_pageLoadState.commitChanges(); m_loaderClient->didFinishProgress(*this); } void WebPageProxy::setNetworkRequestsInProgress(bool networkRequestsInProgress) { auto transaction = m_pageLoadState.transaction(); m_pageLoadState.setNetworkRequestsInProgress(transaction, networkRequestsInProgress); } void WebPageProxy::hasInsecureContent(HasInsecureContent& hasInsecureContent) { hasInsecureContent = m_pageLoadState.committedHasInsecureContent() ? HasInsecureContent::Yes : HasInsecureContent::No; } void WebPageProxy::didDestroyNavigation(uint64_t navigationID) { PageClientProtector protector(m_pageClient); // FIXME: Message check the navigationID. m_navigationState->didDestroyNavigation(navigationID); } void WebPageProxy::didStartProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, const String& url, const String& unreachableURL, const UserData& userData) { PageClientProtector protector(m_pageClient); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.clearPendingAPIRequestURL(transaction); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK_URL(url); // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the page cache. RefPtr navigation; if (frame->isMainFrame() && navigationID) navigation = &navigationState().navigation(navigationID); if (frame->isMainFrame()) { m_pageLoadState.didStartProvisionalLoad(transaction, url, unreachableURL); m_pageClient.didStartProvisionalLoadForMainFrame(); hideValidationMessage(); } frame->setUnreachableURL(unreachableURL); frame->didStartProvisionalLoad(url); m_pageLoadState.commitChanges(); if (m_navigationClient) { if (frame->isMainFrame()) m_navigationClient->didStartProvisionalNavigation(*this, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); } else m_loaderClient->didStartProvisionalLoadForFrame(*this, *frame, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::didReceiveServerRedirectForProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, const String& url, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK_URL(url); // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the page cache. RefPtr navigation; if (frame->isMainFrame() && navigationID) navigation = &navigationState().navigation(navigationID); auto transaction = m_pageLoadState.transaction(); if (frame->isMainFrame()) m_pageLoadState.didReceiveServerRedirectForProvisionalLoad(transaction, url); frame->didReceiveServerRedirectForProvisionalLoad(url); m_pageLoadState.commitChanges(); if (m_navigationClient) { if (frame->isMainFrame()) m_navigationClient->didReceiveServerRedirectForProvisionalNavigation(*this, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); } else m_loaderClient->didReceiveServerRedirectForProvisionalLoadForFrame(*this, *frame, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::didChangeProvisionalURLForFrame(uint64_t frameID, uint64_t, const String& url) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK(frame->frameLoadState().state() == FrameLoadState::State::Provisional); MESSAGE_CHECK_URL(url); auto transaction = m_pageLoadState.transaction(); // Internally, we handle this the same way we handle a server redirect. There are no client callbacks // for this, but if this is the main frame, clients may observe a change to the page's URL. if (frame->isMainFrame()) m_pageLoadState.didReceiveServerRedirectForProvisionalLoad(transaction, url); frame->didReceiveServerRedirectForProvisionalLoad(url); } void WebPageProxy::didFailProvisionalLoadForFrame(uint64_t frameID, const SecurityOriginData& frameSecurityOrigin, uint64_t navigationID, const String& provisionalURL, const ResourceError& error, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the page cache. RefPtr navigation; if (frame->isMainFrame() && navigationID) navigation = navigationState().takeNavigation(navigationID); auto transaction = m_pageLoadState.transaction(); if (frame->isMainFrame()) { m_pageLoadState.didFailProvisionalLoad(transaction); m_pageClient.didFailProvisionalLoadForMainFrame(); } frame->didFailProvisionalLoad(); m_pageLoadState.commitChanges(); ASSERT(!m_failingProvisionalLoadURL); m_failingProvisionalLoadURL = provisionalURL; if (m_navigationClient) { if (frame->isMainFrame()) m_navigationClient->didFailProvisionalNavigationWithError(*this, *frame, navigation.get(), error, m_process->transformHandlesToObjects(userData.object()).get()); else { // FIXME: Get the main frame's current navigation. m_navigationClient->didFailProvisionalLoadInSubframeWithError(*this, *frame, frameSecurityOrigin, nullptr, error, m_process->transformHandlesToObjects(userData.object()).get()); } } else m_loaderClient->didFailProvisionalLoadWithErrorForFrame(*this, *frame, navigation.get(), error, m_process->transformHandlesToObjects(userData.object()).get()); m_failingProvisionalLoadURL = { }; } void WebPageProxy::clearLoadDependentCallbacks() { Vector callbackIDsCopy; copyToVector(m_loadDependentStringCallbackIDs, callbackIDsCopy); m_loadDependentStringCallbackIDs.clear(); for (size_t i = 0; i < callbackIDsCopy.size(); ++i) { auto callback = m_callbacks.take(callbackIDsCopy[i]); if (callback) callback->invalidate(); } } void WebPageProxy::didCommitLoadForFrame(uint64_t frameID, uint64_t navigationID, const String& mimeType, bool frameHasCustomContentProvider, uint32_t opaqueFrameLoadType, const WebCore::CertificateInfo& certificateInfo, bool containsPluginDocument, std::optional hasInsecureContent, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the page cache. RefPtr navigation; if (frame->isMainFrame() && navigationID) navigation = &navigationState().navigation(navigationID); #if PLATFORM(IOS) if (frame->isMainFrame()) { m_hasReceivedLayerTreeTransactionAfterDidCommitLoad = false; m_firstLayerTreeTransactionIdAfterDidCommitLoad = downcast(*drawingArea()).nextLayerTreeTransactionID(); } #endif auto transaction = m_pageLoadState.transaction(); Ref webCertificateInfo = WebCertificateInfo::create(certificateInfo); bool markPageInsecure = hasInsecureContent ? hasInsecureContent.value() == HasInsecureContent::Yes : m_treatsSHA1CertificatesAsInsecure && certificateInfo.containsNonRootSHA1SignedCertificate(); if (frame->isMainFrame()) { m_pageLoadState.didCommitLoad(transaction, webCertificateInfo, markPageInsecure); m_suppressAutomaticNavigationSnapshotting = false; } else if (markPageInsecure) m_pageLoadState.didDisplayOrRunInsecureContent(transaction); #if USE(APPKIT) // FIXME (bug 59111): didCommitLoadForFrame comes too late when restoring a page from b/f cache, making us disable secure event mode in password fields. // FIXME: A load going on in one frame shouldn't affect text editing in other frames on the page. m_pageClient.resetSecureInputState(); m_pageClient.dismissContentRelativeChildWindows(); #endif clearLoadDependentCallbacks(); frame->didCommitLoad(mimeType, webCertificateInfo, containsPluginDocument); if (frame->isMainFrame()) { m_mainFrameHasCustomContentProvider = frameHasCustomContentProvider; if (m_mainFrameHasCustomContentProvider) { // Always assume that the main frame is pinned here, since the custom representation view will handle // any wheel events and dispatch them to the WKView when necessary. m_mainFrameIsPinnedToLeftSide = true; m_mainFrameIsPinnedToRightSide = true; m_mainFrameIsPinnedToTopSide = true; m_mainFrameIsPinnedToBottomSide = true; m_uiClient->pinnedStateDidChange(*this); } m_pageClient.didCommitLoadForMainFrame(mimeType, frameHasCustomContentProvider); } // Even if WebPage has the default pageScaleFactor (and therefore doesn't reset it), // WebPageProxy's cache of the value can get out of sync (e.g. in the case where a // plugin is handling page scaling itself) so we should reset it to the default // for standard main frame loads. if (frame->isMainFrame()) { if (static_cast(opaqueFrameLoadType) == FrameLoadType::Standard) { m_pageScaleFactor = 1; m_pluginScaleFactor = 1; m_mainFramePluginHandlesPageScaleGesture = false; } } #if ENABLE(POINTER_LOCK) if (frame->isMainFrame()) requestPointerUnlock(); #endif m_pageLoadState.commitChanges(); if (m_navigationClient) { if (frame->isMainFrame()) m_navigationClient->didCommitNavigation(*this, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); } else m_loaderClient->didCommitLoadForFrame(*this, *frame, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::didFinishDocumentLoadForFrame(uint64_t frameID, uint64_t navigationID, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the page cache. RefPtr navigation; if (frame->isMainFrame() && navigationID) navigation = &navigationState().navigation(navigationID); if (m_navigationClient) { if (frame->isMainFrame()) m_navigationClient->didFinishDocumentLoad(*this, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); } else m_loaderClient->didFinishDocumentLoadForFrame(*this, *frame, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::didFinishLoadForFrame(uint64_t frameID, uint64_t navigationID, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the page cache. RefPtr navigation; if (frame->isMainFrame() && navigationID) navigation = &navigationState().navigation(navigationID); auto transaction = m_pageLoadState.transaction(); bool isMainFrame = frame->isMainFrame(); if (isMainFrame) m_pageLoadState.didFinishLoad(transaction); if (isMainFrame && m_controlledByAutomation) { if (auto* automationSession = process().processPool().automationSession()) automationSession->navigationOccurredForPage(*this); } frame->didFinishLoad(); m_pageLoadState.commitChanges(); if (m_navigationClient) { if (isMainFrame) m_navigationClient->didFinishNavigation(*this, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); } else m_loaderClient->didFinishLoadForFrame(*this, *frame, navigation.get(), m_process->transformHandlesToObjects(userData.object()).get()); if (isMainFrame) m_pageClient.didFinishLoadForMainFrame(); m_isLoadingAlternateHTMLStringForFailingProvisionalLoad = false; } void WebPageProxy::didFailLoadForFrame(uint64_t frameID, uint64_t navigationID, const ResourceError& error, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the page cache. RefPtr navigation; if (frame->isMainFrame() && navigationID) navigation = &navigationState().navigation(navigationID); clearLoadDependentCallbacks(); auto transaction = m_pageLoadState.transaction(); bool isMainFrame = frame->isMainFrame(); if (isMainFrame) m_pageLoadState.didFailLoad(transaction); if (isMainFrame && m_controlledByAutomation) { if (auto* automationSession = process().processPool().automationSession()) automationSession->navigationOccurredForPage(*this); } frame->didFailLoad(); m_pageLoadState.commitChanges(); if (m_navigationClient) { if (frame->isMainFrame()) m_navigationClient->didFailNavigationWithError(*this, *frame, navigation.get(), error, m_process->transformHandlesToObjects(userData.object()).get()); } else m_loaderClient->didFailLoadWithErrorForFrame(*this, *frame, navigation.get(), error, m_process->transformHandlesToObjects(userData.object()).get()); if (isMainFrame) m_pageClient.didFailLoadForMainFrame(); } void WebPageProxy::didSameDocumentNavigationForFrame(uint64_t frameID, uint64_t navigationID, uint32_t opaqueSameDocumentNavigationType, const String& url, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK_URL(url); // FIXME: We should message check that navigationID is not zero here, but it's currently zero for some navigations through the page cache. RefPtr navigation; if (frame->isMainFrame() && navigationID) navigation = &navigationState().navigation(navigationID); auto transaction = m_pageLoadState.transaction(); bool isMainFrame = frame->isMainFrame(); if (isMainFrame) m_pageLoadState.didSameDocumentNavigation(transaction, url); if (isMainFrame && m_controlledByAutomation) { if (auto* automationSession = process().processPool().automationSession()) automationSession->navigationOccurredForPage(*this); } m_pageLoadState.clearPendingAPIRequestURL(transaction); frame->didSameDocumentNavigation(url); m_pageLoadState.commitChanges(); SameDocumentNavigationType navigationType = static_cast(opaqueSameDocumentNavigationType); if (m_navigationClient) { if (isMainFrame) m_navigationClient->didSameDocumentNavigation(*this, navigation.get(), navigationType, m_process->transformHandlesToObjects(userData.object()).get()); } else m_loaderClient->didSameDocumentNavigationForFrame(*this, *frame, navigation.get(), navigationType, m_process->transformHandlesToObjects(userData.object()).get()); if (isMainFrame) m_pageClient.didSameDocumentNavigationForMainFrame(navigationType); } void WebPageProxy::didReceiveTitleForFrame(uint64_t frameID, const String& title, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); auto transaction = m_pageLoadState.transaction(); if (frame->isMainFrame()) m_pageLoadState.setTitle(transaction, title); frame->didChangeTitle(title); m_pageLoadState.commitChanges(); m_loaderClient->didReceiveTitleForFrame(*this, title, *frame, m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::didFirstLayoutForFrame(uint64_t frameID, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); m_loaderClient->didFirstLayoutForFrame(*this, *frame, m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::didFirstVisuallyNonEmptyLayoutForFrame(uint64_t frameID, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); m_loaderClient->didFirstVisuallyNonEmptyLayoutForFrame(*this, *frame, m_process->transformHandlesToObjects(userData.object()).get()); if (frame->isMainFrame()) m_pageClient.didFirstVisuallyNonEmptyLayoutForMainFrame(); } void WebPageProxy::didLayoutForCustomContentProvider() { didReachLayoutMilestone(DidFirstLayout | DidFirstVisuallyNonEmptyLayout | DidHitRelevantRepaintedObjectsAreaThreshold); } void WebPageProxy::didReachLayoutMilestone(uint32_t layoutMilestones) { PageClientProtector protector(m_pageClient); if (m_navigationClient) m_navigationClient->renderingProgressDidChange(*this, static_cast(layoutMilestones)); else m_loaderClient->didReachLayoutMilestone(*this, static_cast(layoutMilestones)); } void WebPageProxy::didDisplayInsecureContentForFrame(uint64_t frameID, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.didDisplayOrRunInsecureContent(transaction); m_pageLoadState.commitChanges(); m_loaderClient->didDisplayInsecureContentForFrame(*this, *frame, m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::didRunInsecureContentForFrame(uint64_t frameID, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); auto transaction = m_pageLoadState.transaction(); m_pageLoadState.didDisplayOrRunInsecureContent(transaction); m_pageLoadState.commitChanges(); m_loaderClient->didRunInsecureContentForFrame(*this, *frame, m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::didDetectXSSForFrame(uint64_t frameID, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); m_loaderClient->didDetectXSSForFrame(*this, *frame, m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::mainFramePluginHandlesPageScaleGestureDidChange(bool mainFramePluginHandlesPageScaleGesture) { m_mainFramePluginHandlesPageScaleGesture = mainFramePluginHandlesPageScaleGesture; } void WebPageProxy::frameDidBecomeFrameSet(uint64_t frameID, bool value) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); frame->setIsFrameSet(value); if (frame->isMainFrame()) m_frameSetLargestFrame = value ? m_mainFrame : 0; } void WebPageProxy::decidePolicyForNavigationAction(uint64_t frameID, const SecurityOriginData& frameSecurityOrigin, uint64_t navigationID, const NavigationActionData& navigationActionData, uint64_t originatingFrameID, const SecurityOriginData& originatingFrameSecurityOrigin, const WebCore::ResourceRequest& originalRequest, const ResourceRequest& request, uint64_t listenerID, const UserData& userData, bool& receivedPolicyAction, uint64_t& newNavigationID, uint64_t& policyAction, DownloadID& downloadID, WebsitePolicies& websitePolicies) { PageClientProtector protector(m_pageClient); auto transaction = m_pageLoadState.transaction(); if (request.url() != m_pageLoadState.pendingAPIRequestURL()) m_pageLoadState.clearPendingAPIRequestURL(transaction); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK_URL(request.url()); MESSAGE_CHECK_URL(originalRequest.url()); WebFrameProxy* originatingFrame = m_process->webFrame(originatingFrameID); Ref listener = frame->setUpPolicyListenerProxy(listenerID); if (!navigationID && frame->isMainFrame()) { auto navigation = m_navigationState->createLoadRequestNavigation(request); newNavigationID = navigation->navigationID(); listener->setNavigation(WTFMove(navigation)); } #if ENABLE(CONTENT_FILTERING) if (frame->didHandleContentFilterUnblockNavigation(request)) { receivedPolicyAction = true; policyAction = PolicyIgnore; return; } #endif ASSERT(!m_inDecidePolicyForNavigationAction); m_inDecidePolicyForNavigationAction = true; m_syncNavigationActionPolicyActionIsValid = false; #if ENABLE(DOWNLOAD_ATTRIBUTE) m_syncNavigationActionHasDownloadAttribute = !navigationActionData.downloadAttribute.isNull(); #endif if (m_navigationClient) { RefPtr destinationFrameInfo; RefPtr sourceFrameInfo; if (frame) destinationFrameInfo = API::FrameInfo::create(*frame, frameSecurityOrigin.securityOrigin()); if (originatingFrame == frame) sourceFrameInfo = destinationFrameInfo; else if (originatingFrame) sourceFrameInfo = API::FrameInfo::create(*originatingFrame, originatingFrameSecurityOrigin.securityOrigin()); auto userInitiatedActivity = m_process->userInitiatedActivity(navigationActionData.userGestureTokenIdentifier); bool shouldOpenAppLinks = !m_shouldSuppressAppLinksInNextNavigationPolicyDecision && (!destinationFrameInfo || destinationFrameInfo->isMainFrame()) && !hostsAreEqual(URL(ParsedURLString, m_mainFrame->url()), request.url()) && navigationActionData.navigationType != WebCore::NavigationType::BackForward; auto navigationAction = API::NavigationAction::create(navigationActionData, sourceFrameInfo.get(), destinationFrameInfo.get(), request, originalRequest.url(), shouldOpenAppLinks, WTFMove(userInitiatedActivity)); m_navigationClient->decidePolicyForNavigationAction(*this, navigationAction.get(), WTFMove(listener), m_process->transformHandlesToObjects(userData.object()).get()); } else m_policyClient->decidePolicyForNavigationAction(*this, frame, navigationActionData, originatingFrame, originalRequest, request, WTFMove(listener), m_process->transformHandlesToObjects(userData.object()).get()); m_shouldSuppressAppLinksInNextNavigationPolicyDecision = false; m_inDecidePolicyForNavigationAction = false; // Check if we received a policy decision already. If we did, we can just pass it back. receivedPolicyAction = m_syncNavigationActionPolicyActionIsValid; if (m_syncNavigationActionPolicyActionIsValid) { policyAction = m_syncNavigationActionPolicyAction; downloadID = m_syncNavigationActionPolicyDownloadID; websitePolicies = m_syncNavigationActionPolicyWebsitePolicies; } } void WebPageProxy::decidePolicyForNewWindowAction(uint64_t frameID, const SecurityOriginData& frameSecurityOrigin, const NavigationActionData& navigationActionData, const ResourceRequest& request, const String& frameName, uint64_t listenerID, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK_URL(request.url()); Ref listener = frame->setUpPolicyListenerProxy(listenerID); if (m_navigationClient) { RefPtr sourceFrameInfo; if (frame) sourceFrameInfo = API::FrameInfo::create(*frame, frameSecurityOrigin.securityOrigin()); auto userInitiatedActivity = m_process->userInitiatedActivity(navigationActionData.userGestureTokenIdentifier); bool shouldOpenAppLinks = !hostsAreEqual(URL(ParsedURLString, m_mainFrame->url()), request.url()); auto navigationAction = API::NavigationAction::create(navigationActionData, sourceFrameInfo.get(), nullptr, request, request.url(), shouldOpenAppLinks, WTFMove(userInitiatedActivity)); m_navigationClient->decidePolicyForNavigationAction(*this, navigationAction.get(), WTFMove(listener), m_process->transformHandlesToObjects(userData.object()).get()); } else m_policyClient->decidePolicyForNewWindowAction(*this, *frame, navigationActionData, request, frameName, WTFMove(listener), m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::decidePolicyForResponse(uint64_t frameID, const SecurityOriginData& frameSecurityOrigin, const ResourceResponse& response, const ResourceRequest& request, bool canShowMIMEType, uint64_t listenerID, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK_URL(request.url()); MESSAGE_CHECK_URL(response.url()); Ref listener = frame->setUpPolicyListenerProxy(listenerID); if (m_navigationClient) { auto navigationResponse = API::NavigationResponse::create(API::FrameInfo::create(*frame, frameSecurityOrigin.securityOrigin()).get(), request, response, canShowMIMEType); m_navigationClient->decidePolicyForNavigationResponse(*this, navigationResponse.get(), WTFMove(listener), m_process->transformHandlesToObjects(userData.object()).get()); } else m_policyClient->decidePolicyForResponse(*this, *frame, response, request, canShowMIMEType, WTFMove(listener), m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::decidePolicyForResponseSync(uint64_t frameID, const SecurityOriginData& frameSecurityOrigin, const ResourceResponse& response, const ResourceRequest& request, bool canShowMIMEType, uint64_t listenerID, const UserData& userData, bool& receivedPolicyAction, uint64_t& policyAction, DownloadID& downloadID) { PageClientProtector protector(m_pageClient); ASSERT(!m_inDecidePolicyForResponseSync); m_inDecidePolicyForResponseSync = true; m_decidePolicyForResponseRequest = &request; m_syncMimeTypePolicyActionIsValid = false; decidePolicyForResponse(frameID, frameSecurityOrigin, response, request, canShowMIMEType, listenerID, userData); m_inDecidePolicyForResponseSync = false; m_decidePolicyForResponseRequest = 0; // Check if we received a policy decision already. If we did, we can just pass it back. receivedPolicyAction = m_syncMimeTypePolicyActionIsValid; if (m_syncMimeTypePolicyActionIsValid) { policyAction = m_syncMimeTypePolicyAction; downloadID = m_syncMimeTypePolicyDownloadID; } } void WebPageProxy::unableToImplementPolicy(uint64_t frameID, const ResourceError& error, const UserData& userData) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); m_policyClient->unableToImplementPolicy(*this, *frame, error, m_process->transformHandlesToObjects(userData.object()).get()); } // FormClient void WebPageProxy::willSubmitForm(uint64_t frameID, uint64_t sourceFrameID, const Vector>& textFieldValues, uint64_t listenerID, const UserData& userData) { WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); WebFrameProxy* sourceFrame = m_process->webFrame(sourceFrameID); MESSAGE_CHECK(sourceFrame); Ref listener = frame->setUpFormSubmissionListenerProxy(listenerID); m_formClient->willSubmitForm(*this, *frame, *sourceFrame, textFieldValues, m_process->transformHandlesToObjects(userData.object()).get(), listener.get()); } void WebPageProxy::didNavigateWithNavigationData(const WebNavigationDataStore& store, uint64_t frameID) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK(frame->page() == this); if (m_historyClient) { if (frame->isMainFrame()) m_historyClient->didNavigateWithNavigationData(*this, store); } else m_loaderClient->didNavigateWithNavigationData(*this, store, *frame); process().processPool().historyClient().didNavigateWithNavigationData(process().processPool(), *this, store, *frame); } void WebPageProxy::didPerformClientRedirect(const String& sourceURLString, const String& destinationURLString, uint64_t frameID) { PageClientProtector protector(m_pageClient); if (sourceURLString.isEmpty() || destinationURLString.isEmpty()) return; WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK(frame->page() == this); MESSAGE_CHECK_URL(sourceURLString); MESSAGE_CHECK_URL(destinationURLString); if (m_historyClient) { if (frame->isMainFrame()) m_historyClient->didPerformClientRedirect(*this, sourceURLString, destinationURLString); } else m_loaderClient->didPerformClientRedirect(*this, sourceURLString, destinationURLString, *frame); process().processPool().historyClient().didPerformClientRedirect(process().processPool(), *this, sourceURLString, destinationURLString, *frame); } void WebPageProxy::didPerformServerRedirect(const String& sourceURLString, const String& destinationURLString, uint64_t frameID) { PageClientProtector protector(m_pageClient); if (sourceURLString.isEmpty() || destinationURLString.isEmpty()) return; WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK(frame->page() == this); MESSAGE_CHECK_URL(sourceURLString); MESSAGE_CHECK_URL(destinationURLString); if (m_historyClient) { if (frame->isMainFrame()) m_historyClient->didPerformServerRedirect(*this, sourceURLString, destinationURLString); } else m_loaderClient->didPerformServerRedirect(*this, sourceURLString, destinationURLString, *frame); process().processPool().historyClient().didPerformServerRedirect(process().processPool(), *this, sourceURLString, destinationURLString, *frame); } void WebPageProxy::didUpdateHistoryTitle(const String& title, const String& url, uint64_t frameID) { PageClientProtector protector(m_pageClient); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); MESSAGE_CHECK(frame->page() == this); MESSAGE_CHECK_URL(url); if (m_historyClient) { if (frame->isMainFrame()) m_historyClient->didUpdateHistoryTitle(*this, title, url); } else m_loaderClient->didUpdateHistoryTitle(*this, title, url, *frame); process().processPool().historyClient().didUpdateHistoryTitle(process().processPool(), *this, title, url, *frame); } // UIClient void WebPageProxy::createNewPage(uint64_t frameID, const SecurityOriginData& securityOriginData, const ResourceRequest& request, const WindowFeatures& windowFeatures, const NavigationActionData& navigationActionData, uint64_t& newPageID, WebPageCreationParameters& newPageParameters) { WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); auto mainFrameURL = m_mainFrame->url(); RefPtr newPage = m_uiClient->createNewPage(this, frame, securityOriginData, request, windowFeatures, navigationActionData); if (!newPage) { newPageID = 0; return; } newPageID = newPage->pageID(); newPageParameters = newPage->creationParameters(); WebsiteDataStore::cloneSessionData(*this, *newPage); newPage->m_shouldSuppressAppLinksInNextNavigationPolicyDecision = hostsAreEqual(URL(ParsedURLString, mainFrameURL), request.url()); } void WebPageProxy::showPage() { m_uiClient->showPage(this); } void WebPageProxy::fullscreenMayReturnToInline() { m_uiClient->fullscreenMayReturnToInline(this); } void WebPageProxy::didEnterFullscreen() { m_uiClient->didEnterFullscreen(this); } void WebPageProxy::didExitFullscreen() { m_uiClient->didExitFullscreen(this); } void WebPageProxy::closePage(bool stopResponsivenessTimer) { if (stopResponsivenessTimer) m_process->responsivenessTimer().stop(); m_pageClient.clearAllEditCommands(); m_uiClient->close(this); } void WebPageProxy::runJavaScriptAlert(uint64_t frameID, const SecurityOriginData& securityOrigin, const String& message, Ref&& reply) { WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // Since runJavaScriptAlert() can spin a nested run loop we need to turn off the responsiveness timer. m_process->responsivenessTimer().stop(); m_uiClient->runJavaScriptAlert(this, message, frame, securityOrigin, [reply = WTFMove(reply)] { reply->send(); }); } void WebPageProxy::runJavaScriptConfirm(uint64_t frameID, const SecurityOriginData& securityOrigin, const String& message, Ref&& reply) { WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // Since runJavaScriptConfirm() can spin a nested run loop we need to turn off the responsiveness timer. m_process->responsivenessTimer().stop(); m_uiClient->runJavaScriptConfirm(this, message, frame, securityOrigin, [reply = WTFMove(reply)](bool result) { reply->send(result); }); } void WebPageProxy::runJavaScriptPrompt(uint64_t frameID, const SecurityOriginData& securityOrigin, const String& message, const String& defaultValue, RefPtr reply) { WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // Since runJavaScriptPrompt() can spin a nested run loop we need to turn off the responsiveness timer. m_process->responsivenessTimer().stop(); m_uiClient->runJavaScriptPrompt(this, message, defaultValue, frame, securityOrigin, [reply](const String& result) { reply->send(result); }); } void WebPageProxy::setStatusText(const String& text) { m_uiClient->setStatusText(this, text); } void WebPageProxy::mouseDidMoveOverElement(const WebHitTestResultData& hitTestResultData, uint32_t opaqueModifiers, const UserData& userData) { m_lastMouseMoveHitTestResult = API::HitTestResult::create(hitTestResultData); WebEvent::Modifiers modifiers = static_cast(opaqueModifiers); m_uiClient->mouseDidMoveOverElement(this, hitTestResultData, modifiers, m_process->transformHandlesToObjects(userData.object()).get()); } void WebPageProxy::connectionWillOpen(IPC::Connection& connection) { ASSERT(&connection == m_process->connection()); m_webProcessLifetimeTracker.connectionWillOpen(connection); } void WebPageProxy::webProcessWillShutDown() { m_webProcessLifetimeTracker.webProcessWillShutDown(); } void WebPageProxy::processDidFinishLaunching() { ASSERT(m_process->state() == WebProcessProxy::State::Running); finishInitializingWebPageAfterProcessLaunch(); } #if ENABLE(NETSCAPE_PLUGIN_API) void WebPageProxy::unavailablePluginButtonClicked(uint32_t opaquePluginUnavailabilityReason, const String& mimeType, const String& pluginURLString, const String& pluginspageAttributeURLString, const String& frameURLString, const String& pageURLString) { MESSAGE_CHECK_URL(pluginURLString); MESSAGE_CHECK_URL(pluginspageAttributeURLString); MESSAGE_CHECK_URL(frameURLString); MESSAGE_CHECK_URL(pageURLString); RefPtr pluginInformation; String newMimeType = mimeType; PluginModuleInfo plugin = m_process->processPool().pluginInfoStore().findPlugin(newMimeType, URL(URL(), pluginURLString)); pluginInformation = createPluginInformationDictionary(plugin, frameURLString, mimeType, pageURLString, pluginspageAttributeURLString, pluginURLString); WKPluginUnavailabilityReason pluginUnavailabilityReason = kWKPluginUnavailabilityReasonPluginMissing; switch (static_cast(opaquePluginUnavailabilityReason)) { case RenderEmbeddedObject::PluginMissing: pluginUnavailabilityReason = kWKPluginUnavailabilityReasonPluginMissing; break; case RenderEmbeddedObject::InsecurePluginVersion: pluginUnavailabilityReason = kWKPluginUnavailabilityReasonInsecurePluginVersion; break; case RenderEmbeddedObject::PluginCrashed: pluginUnavailabilityReason = kWKPluginUnavailabilityReasonPluginCrashed; break; case RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy: ASSERT_NOT_REACHED(); } m_uiClient->unavailablePluginButtonClicked(this, pluginUnavailabilityReason, pluginInformation.get()); } #endif // ENABLE(NETSCAPE_PLUGIN_API) #if ENABLE(WEBGL) void WebPageProxy::webGLPolicyForURL(const String& url, uint32_t& loadPolicy) { loadPolicy = static_cast(m_loaderClient->webGLLoadPolicy(*this, url)); } void WebPageProxy::resolveWebGLPolicyForURL(const String& url, uint32_t& loadPolicy) { loadPolicy = static_cast(m_loaderClient->resolveWebGLLoadPolicy(*this, url)); } #endif // ENABLE(WEBGL) void WebPageProxy::setToolbarsAreVisible(bool toolbarsAreVisible) { m_uiClient->setToolbarsAreVisible(this, toolbarsAreVisible); } void WebPageProxy::getToolbarsAreVisible(bool& toolbarsAreVisible) { toolbarsAreVisible = m_uiClient->toolbarsAreVisible(this); } void WebPageProxy::setMenuBarIsVisible(bool menuBarIsVisible) { m_uiClient->setMenuBarIsVisible(this, menuBarIsVisible); } void WebPageProxy::getMenuBarIsVisible(bool& menuBarIsVisible) { menuBarIsVisible = m_uiClient->menuBarIsVisible(this); } void WebPageProxy::setStatusBarIsVisible(bool statusBarIsVisible) { m_uiClient->setStatusBarIsVisible(this, statusBarIsVisible); } void WebPageProxy::getStatusBarIsVisible(bool& statusBarIsVisible) { statusBarIsVisible = m_uiClient->statusBarIsVisible(this); } void WebPageProxy::setIsResizable(bool isResizable) { m_uiClient->setIsResizable(this, isResizable); } void WebPageProxy::getIsResizable(bool& isResizable) { isResizable = m_uiClient->isResizable(this); } void WebPageProxy::setWindowFrame(const FloatRect& newWindowFrame) { m_uiClient->setWindowFrame(this, m_pageClient.convertToDeviceSpace(newWindowFrame)); } void WebPageProxy::getWindowFrame(FloatRect& newWindowFrame) { newWindowFrame = m_pageClient.convertToUserSpace(m_uiClient->windowFrame(this)); } void WebPageProxy::screenToRootView(const IntPoint& screenPoint, IntPoint& windowPoint) { windowPoint = m_pageClient.screenToRootView(screenPoint); } void WebPageProxy::rootViewToScreen(const IntRect& viewRect, IntRect& result) { result = m_pageClient.rootViewToScreen(viewRect); } #if PLATFORM(IOS) void WebPageProxy::accessibilityScreenToRootView(const IntPoint& screenPoint, IntPoint& windowPoint) { windowPoint = m_pageClient.accessibilityScreenToRootView(screenPoint); } void WebPageProxy::rootViewToAccessibilityScreen(const IntRect& viewRect, IntRect& result) { result = m_pageClient.rootViewToAccessibilityScreen(viewRect); } #endif void WebPageProxy::runBeforeUnloadConfirmPanel(const String& message, uint64_t frameID, RefPtr reply) { WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // Since runBeforeUnloadConfirmPanel() can spin a nested run loop we need to turn off the responsiveness timer. m_process->responsivenessTimer().stop(); m_uiClient->runBeforeUnloadConfirmPanel(this, message, frame, [reply](bool result) { reply->send(result); }); } void WebPageProxy::didChangeViewportProperties(const ViewportAttributes& attr) { m_pageClient.didChangeViewportProperties(attr); } void WebPageProxy::pageDidScroll() { m_uiClient->pageDidScroll(this); #if PLATFORM(IOS) // Do not hide the validation message if the scrolling was caused by the keyboard showing up. if (m_isKeyboardAnimatingIn) return; #endif hideValidationMessage(); } void WebPageProxy::runOpenPanel(uint64_t frameID, const SecurityOriginData& frameSecurityOrigin, const FileChooserSettings& settings) { if (m_openPanelResultListener) { m_openPanelResultListener->invalidate(); m_openPanelResultListener = nullptr; } WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); Ref parameters = API::OpenPanelParameters::create(settings); m_openPanelResultListener = WebOpenPanelResultListenerProxy::create(this); // Since runOpenPanel() can spin a nested run loop we need to turn off the responsiveness timer. m_process->responsivenessTimer().stop(); if (!m_uiClient->runOpenPanel(this, frame, frameSecurityOrigin, parameters.ptr(), m_openPanelResultListener.get())) { if (!m_pageClient.handleRunOpenPanel(this, frame, parameters.ptr(), m_openPanelResultListener.get())) didCancelForOpenPanel(); } } void WebPageProxy::printFrame(uint64_t frameID) { ASSERT(!m_isPerformingDOMPrintOperation); m_isPerformingDOMPrintOperation = true; WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); m_uiClient->printFrame(this, frame); endPrinting(); // Send a message synchronously while m_isPerformingDOMPrintOperation is still true. m_isPerformingDOMPrintOperation = false; } void WebPageProxy::printMainFrame() { printFrame(m_mainFrame->frameID()); } void WebPageProxy::setMediaVolume(float volume) { if (volume == m_mediaVolume) return; m_mediaVolume = volume; if (!isValid()) return; m_process->send(Messages::WebPage::SetMediaVolume(volume), m_pageID); } void WebPageProxy::setMuted(WebCore::MediaProducer::MutedStateFlags state) { if (m_mutedState == state) return; m_mutedState = state; if (!isValid()) return; m_process->send(Messages::WebPage::SetMuted(state), m_pageID); activityStateDidChange(ActivityState::IsAudible); } #if ENABLE(MEDIA_SESSION) void WebPageProxy::handleMediaEvent(MediaEventType eventType) { if (!isValid()) return; m_process->send(Messages::WebPage::HandleMediaEvent(eventType), m_pageID); } void WebPageProxy::setVolumeOfMediaElement(double volume, uint64_t elementID) { if (!isValid()) return; m_process->send(Messages::WebPage::SetVolumeOfMediaElement(volume, elementID), m_pageID); } #endif void WebPageProxy::setMayStartMediaWhenInWindow(bool mayStartMedia) { if (mayStartMedia == m_mayStartMediaWhenInWindow) return; m_mayStartMediaWhenInWindow = mayStartMedia; if (!isValid()) return; process().send(Messages::WebPage::SetMayStartMediaWhenInWindow(mayStartMedia), m_pageID); } void WebPageProxy::handleDownloadRequest(DownloadProxy* download) { m_pageClient.handleDownloadRequest(download); } void WebPageProxy::didChangeContentSize(const IntSize& size) { m_pageClient.didChangeContentSize(size); } #if ENABLE(INPUT_TYPE_COLOR) void WebPageProxy::showColorPicker(const WebCore::Color& initialColor, const IntRect& elementRect) { #if ENABLE(INPUT_TYPE_COLOR_POPOVER) // A new popover color well needs to be created (and the previous one destroyed) for // each activation of a color element. m_colorPicker = nullptr; #endif if (!m_colorPicker) m_colorPicker = m_pageClient.createColorPicker(this, initialColor, elementRect); m_colorPicker->showColorPicker(initialColor); } void WebPageProxy::setColorPickerColor(const WebCore::Color& color) { ASSERT(m_colorPicker); m_colorPicker->setSelectedColor(color); } void WebPageProxy::endColorPicker() { ASSERT(m_colorPicker); m_colorPicker->endPicker(); } void WebPageProxy::didChooseColor(const WebCore::Color& color) { if (!isValid()) return; m_process->send(Messages::WebPage::DidChooseColor(color), m_pageID); } void WebPageProxy::didEndColorPicker() { if (!isValid()) return; #if ENABLE(INPUT_TYPE_COLOR) if (m_colorPicker) { m_colorPicker->invalidate(); m_colorPicker = nullptr; } #endif m_process->send(Messages::WebPage::DidEndColorPicker(), m_pageID); } #endif // Inspector WebInspectorProxy* WebPageProxy::inspector() const { if (isClosed() || !isValid()) return 0; return m_inspector.get(); } #if ENABLE(FULLSCREEN_API) WebFullScreenManagerProxy* WebPageProxy::fullScreenManager() { return m_fullScreenManager.get(); } void WebPageProxy::setFullscreenClient(std::unique_ptr client) { m_fullscreenClient = WTFMove(client); } #endif #if (PLATFORM(IOS) && HAVE(AVKIT)) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) WebPlaybackSessionManagerProxy* WebPageProxy::playbackSessionManager() { return m_playbackSessionManager.get(); } WebVideoFullscreenManagerProxy* WebPageProxy::videoFullscreenManager() { return m_videoFullscreenManager.get(); } #endif #if PLATFORM(IOS) bool WebPageProxy::allowsMediaDocumentInlinePlayback() const { return m_allowsMediaDocumentInlinePlayback; } void WebPageProxy::setAllowsMediaDocumentInlinePlayback(bool allows) { if (m_allowsMediaDocumentInlinePlayback == allows) return; m_allowsMediaDocumentInlinePlayback = allows; m_process->send(Messages::WebPage::SetAllowsMediaDocumentInlinePlayback(allows), m_pageID); } #endif void WebPageProxy::setHasHadSelectionChangesFromUserInteraction(bool hasHadUserSelectionChanges) { m_hasHadSelectionChangesFromUserInteraction = hasHadUserSelectionChanges; } void WebPageProxy::setNeedsHiddenContentEditableQuirk(bool needsHiddenContentEditableQuirk) { m_needsHiddenContentEditableQuirk = needsHiddenContentEditableQuirk; } void WebPageProxy::setNeedsPlainTextQuirk(bool needsPlainTextQuirk) { m_needsPlainTextQuirk = needsPlainTextQuirk; } // BackForwardList void WebPageProxy::backForwardAddItem(uint64_t itemID) { m_backForwardList->addItem(m_process->webBackForwardItem(itemID)); } void WebPageProxy::backForwardGoToItem(uint64_t itemID, SandboxExtension::Handle& sandboxExtensionHandle) { WebBackForwardListItem* item = m_process->webBackForwardItem(itemID); if (!item) return; bool createdExtension = maybeInitializeSandboxExtensionHandle(URL(URL(), item->url()), sandboxExtensionHandle); if (createdExtension) m_process->willAcquireUniversalFileReadSandboxExtension(); m_backForwardList->goToItem(item); } void WebPageProxy::backForwardItemAtIndex(int32_t index, uint64_t& itemID) { WebBackForwardListItem* item = m_backForwardList->itemAtIndex(index); itemID = item ? item->itemID() : 0; } void WebPageProxy::backForwardBackListCount(int32_t& count) { count = m_backForwardList->backListCount(); } void WebPageProxy::backForwardForwardListCount(int32_t& count) { count = m_backForwardList->forwardListCount(); } void WebPageProxy::compositionWasCanceled() { #if PLATFORM(COCOA) m_pageClient.notifyInputContextAboutDiscardedComposition(); #endif } // Undo management void WebPageProxy::registerEditCommandForUndo(uint64_t commandID, uint32_t editAction) { registerEditCommand(WebEditCommandProxy::create(commandID, static_cast(editAction), this), Undo); } void WebPageProxy::registerInsertionUndoGrouping() { #if USE(INSERTION_UNDO_GROUPING) m_pageClient.registerInsertionUndoGrouping(); #endif } void WebPageProxy::canUndoRedo(uint32_t action, bool& result) { result = m_pageClient.canUndoRedo(static_cast(action)); } void WebPageProxy::executeUndoRedo(uint32_t action, bool& result) { m_pageClient.executeUndoRedo(static_cast(action)); result = true; } void WebPageProxy::clearAllEditCommands() { m_pageClient.clearAllEditCommands(); } void WebPageProxy::didCountStringMatches(const String& string, uint32_t matchCount) { m_findClient->didCountStringMatches(this, string, matchCount); } void WebPageProxy::didGetImageForFindMatch(const ShareableBitmap::Handle& contentImageHandle, uint32_t matchIndex) { auto bitmap = ShareableBitmap::create(contentImageHandle); if (!bitmap) { ASSERT_NOT_REACHED(); return; } m_findMatchesClient->didGetImageForMatchResult(this, WebImage::create(bitmap.releaseNonNull()).ptr(), matchIndex); } void WebPageProxy::setTextIndicator(const TextIndicatorData& indicatorData, uint64_t lifetime) { // FIXME: Make TextIndicatorWindow a platform-independent presentational thing ("TextIndicatorPresentation"?). #if PLATFORM(COCOA) m_pageClient.setTextIndicator(TextIndicator::create(indicatorData), static_cast(lifetime)); #else ASSERT_NOT_REACHED(); #endif } void WebPageProxy::clearTextIndicator() { #if PLATFORM(COCOA) m_pageClient.clearTextIndicator(TextIndicatorWindowDismissalAnimation::FadeOut); #else ASSERT_NOT_REACHED(); #endif } void WebPageProxy::setTextIndicatorAnimationProgress(float progress) { #if PLATFORM(COCOA) m_pageClient.setTextIndicatorAnimationProgress(progress); #else ASSERT_NOT_REACHED(); #endif } void WebPageProxy::didFindString(const String& string, const Vector& matchRects, uint32_t matchCount, int32_t matchIndex) { m_findClient->didFindString(this, string, matchRects, matchCount, matchIndex); } void WebPageProxy::didFindStringMatches(const String& string, const Vector>& matchRects, int32_t firstIndexAfterSelection) { m_findMatchesClient->didFindStringMatches(this, string, matchRects, firstIndexAfterSelection); } void WebPageProxy::didFailToFindString(const String& string) { m_findClient->didFailToFindString(this, string); } bool WebPageProxy::sendMessage(std::unique_ptr encoder, OptionSet sendOptions) { return m_process->sendMessage(WTFMove(encoder), sendOptions); } IPC::Connection* WebPageProxy::messageSenderConnection() { return m_process->connection(); } uint64_t WebPageProxy::messageSenderDestinationID() { return m_pageID; } void WebPageProxy::valueChangedForPopupMenu(WebPopupMenuProxy*, int32_t newSelectedIndex) { m_process->send(Messages::WebPage::DidChangeSelectedIndexForActivePopupMenu(newSelectedIndex), m_pageID); } void WebPageProxy::setTextFromItemForPopupMenu(WebPopupMenuProxy*, int32_t index) { m_process->send(Messages::WebPage::SetTextForActivePopupMenu(index), m_pageID); } NativeWebMouseEvent* WebPageProxy::currentlyProcessedMouseDownEvent() { return m_currentlyProcessedMouseDownEvent.get(); } void WebPageProxy::postMessageToInjectedBundle(const String& messageName, API::Object* messageBody) { process().send(Messages::WebPage::PostInjectedBundleMessage(messageName, UserData(process().transformObjectsToHandles(messageBody).get())), m_pageID); } #if PLATFORM(GTK) void WebPageProxy::failedToShowPopupMenu() { m_process->send(Messages::WebPage::FailedToShowPopupMenu(), m_pageID); } #endif void WebPageProxy::showPopupMenu(const IntRect& rect, uint64_t textDirection, const Vector& items, int32_t selectedIndex, const PlatformPopupMenuData& data) { if (m_activePopupMenu) { m_activePopupMenu->hidePopupMenu(); m_activePopupMenu->invalidate(); m_activePopupMenu = nullptr; } m_activePopupMenu = m_pageClient.createPopupMenuProxy(*this); if (!m_activePopupMenu) return; // Since showPopupMenu() can spin a nested run loop we need to turn off the responsiveness timer. m_process->responsivenessTimer().stop(); // Showing a popup menu runs a nested runloop, which can handle messages that cause |this| to get closed. Ref protect(*this); m_activePopupMenu->showPopupMenu(rect, static_cast(textDirection), m_pageScaleFactor, items, data, selectedIndex); } void WebPageProxy::hidePopupMenu() { if (!m_activePopupMenu) return; m_activePopupMenu->hidePopupMenu(); m_activePopupMenu->invalidate(); m_activePopupMenu = nullptr; } #if ENABLE(CONTEXT_MENUS) void WebPageProxy::showContextMenu(const ContextMenuContextData& contextMenuContextData, const UserData& userData) { // Showing a context menu runs a nested runloop, which can handle messages that cause |this| to get closed. Ref protect(*this); internalShowContextMenu(contextMenuContextData, userData); // No matter the result of internalShowContextMenu, always notify the WebProcess that the menu is hidden so it starts handling mouse events again. m_process->send(Messages::WebPage::ContextMenuHidden(), m_pageID); } void WebPageProxy::internalShowContextMenu(const ContextMenuContextData& contextMenuContextData, const UserData& userData) { m_activeContextMenuContextData = contextMenuContextData; m_activeContextMenu = m_pageClient.createContextMenuProxy(*this, contextMenuContextData, userData); if (!m_activeContextMenu) return; // Since showContextMenu() can spin a nested run loop we need to turn off the responsiveness timer. m_process->responsivenessTimer().stop(); m_activeContextMenu->show(); } void WebPageProxy::contextMenuItemSelected(const WebContextMenuItemData& item) { // Application custom items don't need to round-trip through to WebCore in the WebProcess. if (item.action() >= ContextMenuItemBaseApplicationTag) { m_contextMenuClient->customContextMenuItemSelected(*this, item); return; } #if PLATFORM(COCOA) if (item.action() == ContextMenuItemTagSmartCopyPaste) { setSmartInsertDeleteEnabled(!isSmartInsertDeleteEnabled()); return; } if (item.action() == ContextMenuItemTagSmartQuotes) { TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled); m_process->updateTextCheckerState(); return; } if (item.action() == ContextMenuItemTagSmartDashes) { TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled); m_process->updateTextCheckerState(); return; } if (item.action() == ContextMenuItemTagSmartLinks) { TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled); m_process->updateTextCheckerState(); return; } if (item.action() == ContextMenuItemTagTextReplacement) { TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled); m_process->updateTextCheckerState(); return; } if (item.action() == ContextMenuItemTagCorrectSpellingAutomatically) { TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled); m_process->updateTextCheckerState(); return; } if (item.action() == ContextMenuItemTagShowSubstitutions) { TextChecker::toggleSubstitutionsPanelIsShowing(); return; } #endif if (item.action() == ContextMenuItemTagDownloadImageToDisk) { m_process->processPool().download(this, URL(URL(), m_activeContextMenuContextData.webHitTestResultData().absoluteImageURL)); return; } if (item.action() == ContextMenuItemTagDownloadLinkToDisk) { auto& hitTestResult = m_activeContextMenuContextData.webHitTestResultData(); m_process->processPool().download(this, URL(URL(), hitTestResult.absoluteLinkURL), hitTestResult.linkSuggestedFilename); return; } if (item.action() == ContextMenuItemTagDownloadMediaToDisk) { m_process->processPool().download(this, URL(URL(), m_activeContextMenuContextData.webHitTestResultData().absoluteMediaURL)); return; } if (item.action() == ContextMenuItemTagCheckSpellingWhileTyping) { TextChecker::setContinuousSpellCheckingEnabled(!TextChecker::state().isContinuousSpellCheckingEnabled); m_process->updateTextCheckerState(); return; } if (item.action() == ContextMenuItemTagCheckGrammarWithSpelling) { TextChecker::setGrammarCheckingEnabled(!TextChecker::state().isGrammarCheckingEnabled); m_process->updateTextCheckerState(); return; } if (item.action() == ContextMenuItemTagShowSpellingPanel) { if (!TextChecker::spellingUIIsShowing()) advanceToNextMisspelling(true); TextChecker::toggleSpellingUIIsShowing(); return; } if (item.action() == ContextMenuItemTagLearnSpelling || item.action() == ContextMenuItemTagIgnoreSpelling) ++m_pendingLearnOrIgnoreWordMessageCount; m_process->send(Messages::WebPage::DidSelectItemFromActiveContextMenu(item), m_pageID); } void WebPageProxy::handleContextMenuKeyEvent() { m_process->send(Messages::WebPage::ContextMenuForKeyEvent(), m_pageID); } #endif // ENABLE(CONTEXT_MENUS) #if PLATFORM(IOS) void WebPageProxy::didChooseFilesForOpenPanelWithDisplayStringAndIcon(const Vector& fileURLs, const String& displayString, const API::Data* iconData) { if (!isValid()) return; #if ENABLE(SANDBOX_EXTENSIONS) // FIXME: The sandbox extensions should be sent with the DidChooseFilesForOpenPanel message. This // is gated on a way of passing SandboxExtension::Handles in a Vector. for (size_t i = 0; i < fileURLs.size(); ++i) { SandboxExtension::Handle sandboxExtensionHandle; SandboxExtension::createHandle(fileURLs[i], SandboxExtension::ReadOnly, sandboxExtensionHandle); m_process->send(Messages::WebPage::ExtendSandboxForFileFromOpenPanel(sandboxExtensionHandle), m_pageID); } #endif m_process->send(Messages::WebPage::DidChooseFilesForOpenPanelWithDisplayStringAndIcon(fileURLs, displayString, iconData ? iconData->dataReference() : IPC::DataReference()), m_pageID); m_openPanelResultListener->invalidate(); m_openPanelResultListener = nullptr; } #endif void WebPageProxy::didChooseFilesForOpenPanel(const Vector& fileURLs) { if (!isValid()) return; #if ENABLE(SANDBOX_EXTENSIONS) // FIXME: The sandbox extensions should be sent with the DidChooseFilesForOpenPanel message. This // is gated on a way of passing SandboxExtension::Handles in a Vector. for (size_t i = 0; i < fileURLs.size(); ++i) { SandboxExtension::Handle sandboxExtensionHandle; bool createdExtension = SandboxExtension::createHandle(fileURLs[i], SandboxExtension::ReadOnly, sandboxExtensionHandle); if (!createdExtension) { // This can legitimately fail if a directory containing the file is deleted after the file was chosen. // We also have reports of cases where this likely fails for some unknown reason, . WTFLogAlways("WebPageProxy::didChooseFilesForOpenPanel: could not create a sandbox extension for '%s'\n", fileURLs[i].utf8().data()); continue; } m_process->send(Messages::WebPage::ExtendSandboxForFileFromOpenPanel(sandboxExtensionHandle), m_pageID); } #endif m_process->send(Messages::WebPage::DidChooseFilesForOpenPanel(fileURLs), m_pageID); m_openPanelResultListener->invalidate(); m_openPanelResultListener = nullptr; } void WebPageProxy::didCancelForOpenPanel() { if (!isValid()) return; m_process->send(Messages::WebPage::DidCancelForOpenPanel(), m_pageID); m_openPanelResultListener->invalidate(); m_openPanelResultListener = nullptr; } void WebPageProxy::advanceToNextMisspelling(bool startBeforeSelection) { m_process->send(Messages::WebPage::AdvanceToNextMisspelling(startBeforeSelection), m_pageID); } void WebPageProxy::changeSpellingToWord(const String& word) { if (word.isEmpty()) return; m_process->send(Messages::WebPage::ChangeSpellingToWord(word), m_pageID); } void WebPageProxy::registerEditCommand(PassRefPtr commandProxy, UndoOrRedo undoOrRedo) { m_pageClient.registerEditCommand(commandProxy, undoOrRedo); } void WebPageProxy::addEditCommand(WebEditCommandProxy* command) { m_editCommandSet.add(command); } void WebPageProxy::removeEditCommand(WebEditCommandProxy* command) { m_editCommandSet.remove(command); if (!isValid()) return; m_process->send(Messages::WebPage::DidRemoveEditCommand(command->commandID()), m_pageID); } bool WebPageProxy::isValidEditCommand(WebEditCommandProxy* command) { return m_editCommandSet.find(command) != m_editCommandSet.end(); } int64_t WebPageProxy::spellDocumentTag() { if (!m_hasSpellDocumentTag) { m_spellDocumentTag = TextChecker::uniqueSpellDocumentTag(this); m_hasSpellDocumentTag = true; } return m_spellDocumentTag; } #if USE(UNIFIED_TEXT_CHECKING) void WebPageProxy::checkTextOfParagraph(const String& text, uint64_t checkingTypes, int32_t insertionPoint, Vector& results) { results = TextChecker::checkTextOfParagraph(spellDocumentTag(), text, insertionPoint, checkingTypes, m_initialCapitalizationEnabled); } #endif void WebPageProxy::checkSpellingOfString(const String& text, int32_t& misspellingLocation, int32_t& misspellingLength) { TextChecker::checkSpellingOfString(spellDocumentTag(), text, misspellingLocation, misspellingLength); } void WebPageProxy::checkGrammarOfString(const String& text, Vector& grammarDetails, int32_t& badGrammarLocation, int32_t& badGrammarLength) { TextChecker::checkGrammarOfString(spellDocumentTag(), text, grammarDetails, badGrammarLocation, badGrammarLength); } void WebPageProxy::spellingUIIsShowing(bool& isShowing) { isShowing = TextChecker::spellingUIIsShowing(); } void WebPageProxy::updateSpellingUIWithMisspelledWord(const String& misspelledWord) { TextChecker::updateSpellingUIWithMisspelledWord(spellDocumentTag(), misspelledWord); } void WebPageProxy::updateSpellingUIWithGrammarString(const String& badGrammarPhrase, const GrammarDetail& grammarDetail) { TextChecker::updateSpellingUIWithGrammarString(spellDocumentTag(), badGrammarPhrase, grammarDetail); } void WebPageProxy::getGuessesForWord(const String& word, const String& context, int32_t insertionPoint, Vector& guesses) { TextChecker::getGuessesForWord(spellDocumentTag(), word, context, insertionPoint, guesses, m_initialCapitalizationEnabled); } void WebPageProxy::learnWord(const String& word) { MESSAGE_CHECK(m_pendingLearnOrIgnoreWordMessageCount); --m_pendingLearnOrIgnoreWordMessageCount; TextChecker::learnWord(spellDocumentTag(), word); } void WebPageProxy::ignoreWord(const String& word) { MESSAGE_CHECK(m_pendingLearnOrIgnoreWordMessageCount); --m_pendingLearnOrIgnoreWordMessageCount; TextChecker::ignoreWord(spellDocumentTag(), word); } void WebPageProxy::requestCheckingOfString(uint64_t requestID, const TextCheckingRequestData& request, int32_t insertionPoint) { TextChecker::requestCheckingOfString(TextCheckerCompletion::create(requestID, request, this), insertionPoint); } void WebPageProxy::didFinishCheckingText(uint64_t requestID, const Vector& result) { m_process->send(Messages::WebPage::DidFinishCheckingText(requestID, result), m_pageID); } void WebPageProxy::didCancelCheckingText(uint64_t requestID) { m_process->send(Messages::WebPage::DidCancelCheckingText(requestID), m_pageID); } // Other void WebPageProxy::setFocus(bool focused) { if (focused) m_uiClient->focus(this); else m_uiClient->unfocus(this); } void WebPageProxy::takeFocus(uint32_t direction) { m_uiClient->takeFocus(this, (static_cast(direction) == FocusDirectionForward) ? kWKFocusDirectionForward : kWKFocusDirectionBackward); } void WebPageProxy::setToolTip(const String& toolTip) { String oldToolTip = m_toolTip; m_toolTip = toolTip; m_pageClient.toolTipChanged(oldToolTip, m_toolTip); } void WebPageProxy::setCursor(const WebCore::Cursor& cursor) { // The Web process may have asked to change the cursor when the view was in an active window, but // if it is no longer in a window or the window is not active, then the cursor should not change. if (m_pageClient.isViewWindowActive()) m_pageClient.setCursor(cursor); } void WebPageProxy::setCursorHiddenUntilMouseMoves(bool hiddenUntilMouseMoves) { m_pageClient.setCursorHiddenUntilMouseMoves(hiddenUntilMouseMoves); } void WebPageProxy::didReceiveEvent(uint32_t opaqueType, bool handled) { WebEvent::Type type = static_cast(opaqueType); switch (type) { case WebEvent::NoType: case WebEvent::MouseMove: case WebEvent::Wheel: break; case WebEvent::MouseDown: case WebEvent::MouseUp: case WebEvent::MouseForceChanged: case WebEvent::MouseForceDown: case WebEvent::MouseForceUp: case WebEvent::KeyDown: case WebEvent::KeyUp: case WebEvent::RawKeyDown: case WebEvent::Char: #if ENABLE(TOUCH_EVENTS) case WebEvent::TouchStart: case WebEvent::TouchMove: case WebEvent::TouchEnd: case WebEvent::TouchCancel: #endif #if ENABLE(MAC_GESTURE_EVENTS) case WebEvent::GestureStart: case WebEvent::GestureChange: case WebEvent::GestureEnd: #endif m_process->responsivenessTimer().stop(); break; } switch (type) { case WebEvent::NoType: break; case WebEvent::MouseMove: m_processingMouseMoveEvent = false; if (m_nextMouseMoveEvent) handleMouseEvent(*std::exchange(m_nextMouseMoveEvent, nullptr)); break; case WebEvent::MouseDown: break; case WebEvent::MouseUp: m_currentlyProcessedMouseDownEvent = nullptr; break; case WebEvent::MouseForceChanged: case WebEvent::MouseForceDown: case WebEvent::MouseForceUp: break; case WebEvent::Wheel: { MESSAGE_CHECK(!m_currentlyProcessedWheelEvents.isEmpty()); std::unique_ptr> oldestCoalescedEvent = m_currentlyProcessedWheelEvents.takeFirst(); // FIXME: Dispatch additional events to the didNotHandleWheelEvent client function. if (!handled) { if (m_uiClient->implementsDidNotHandleWheelEvent()) m_uiClient->didNotHandleWheelEvent(this, oldestCoalescedEvent->last()); m_pageClient.wheelEventWasNotHandledByWebCore(oldestCoalescedEvent->last()); } if (!m_wheelEventQueue.isEmpty()) processNextQueuedWheelEvent(); break; } case WebEvent::KeyDown: case WebEvent::KeyUp: case WebEvent::RawKeyDown: case WebEvent::Char: { LOG(KeyHandling, "WebPageProxy::didReceiveEvent: %s (queue empty %d)", webKeyboardEventTypeString(type), m_keyEventQueue.isEmpty()); MESSAGE_CHECK(!m_keyEventQueue.isEmpty()); NativeWebKeyboardEvent event = m_keyEventQueue.takeFirst(); MESSAGE_CHECK(type == event.type()); if (!m_keyEventQueue.isEmpty()) { LOG(KeyHandling, " UI process: sent keyEvent from didReceiveEvent"); m_process->send(Messages::WebPage::KeyEvent(m_keyEventQueue.first()), m_pageID); } else { if (auto* automationSession = process().processPool().automationSession()) automationSession->keyboardEventsFlushedForPage(*this); } // The call to doneWithKeyEvent may close this WebPage. // Protect against this being destroyed. Ref protect(*this); m_pageClient.doneWithKeyEvent(event, handled); if (handled) break; if (m_uiClient->implementsDidNotHandleKeyEvent()) m_uiClient->didNotHandleKeyEvent(this, event); break; } #if ENABLE(MAC_GESTURE_EVENTS) case WebEvent::GestureStart: case WebEvent::GestureChange: case WebEvent::GestureEnd: { MESSAGE_CHECK(!m_gestureEventQueue.isEmpty()); NativeWebGestureEvent event = m_gestureEventQueue.takeFirst(); MESSAGE_CHECK(type == event.type()); if (!handled) m_pageClient.gestureEventWasNotHandledByWebCore(event); break; } break; #endif #if ENABLE(IOS_TOUCH_EVENTS) case WebEvent::TouchStart: case WebEvent::TouchMove: case WebEvent::TouchEnd: case WebEvent::TouchCancel: break; #elif ENABLE(TOUCH_EVENTS) case WebEvent::TouchStart: case WebEvent::TouchMove: case WebEvent::TouchEnd: case WebEvent::TouchCancel: { MESSAGE_CHECK(!m_touchEventQueue.isEmpty()); QueuedTouchEvents queuedEvents = m_touchEventQueue.takeFirst(); MESSAGE_CHECK(type == queuedEvents.forwardedEvent.type()); m_pageClient.doneWithTouchEvent(queuedEvents.forwardedEvent, handled); for (size_t i = 0; i < queuedEvents.deferredTouchEvents.size(); ++i) { bool isEventHandled = false; m_pageClient.doneWithTouchEvent(queuedEvents.deferredTouchEvents.at(i), isEventHandled); } break; } #endif } } void WebPageProxy::stopResponsivenessTimer() { m_process->responsivenessTimer().stop(); } void WebPageProxy::voidCallback(uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. return; } callback->performCallback(); } void WebPageProxy::dataCallback(const IPC::DataReference& dataReference, uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. return; } callback->performCallbackWithReturnValue(API::Data::create(dataReference.data(), dataReference.size()).ptr()); } void WebPageProxy::imageCallback(const ShareableBitmap::Handle& bitmapHandle, uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. return; } callback->performCallbackWithReturnValue(bitmapHandle); } void WebPageProxy::stringCallback(const String& resultString, uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. // this can validly happen if a load invalidated the callback, though return; } m_loadDependentStringCallbackIDs.remove(callbackID); callback->performCallbackWithReturnValue(resultString.impl()); } void WebPageProxy::invalidateStringCallback(uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. // this can validly happen if a load invalidated the callback, though return; } m_loadDependentStringCallbackIDs.remove(callbackID); callback->invalidate(); } void WebPageProxy::scriptValueCallback(const IPC::DataReference& dataReference, bool hadException, const ExceptionDetails& details, uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. return; } if (dataReference.isEmpty()) { callback->performCallbackWithReturnValue(nullptr, hadException, details); return; } Vector data; data.reserveInitialCapacity(dataReference.size()); data.append(dataReference.data(), dataReference.size()); callback->performCallbackWithReturnValue(API::SerializedScriptValue::adopt(WTFMove(data)).ptr(), hadException, details); } void WebPageProxy::computedPagesCallback(const Vector& pageRects, double totalScaleFactorForPrinting, uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. return; } callback->performCallbackWithReturnValue(pageRects, totalScaleFactorForPrinting); } void WebPageProxy::validateCommandCallback(const String& commandName, bool isEnabled, int state, uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. return; } callback->performCallbackWithReturnValue(commandName.impl(), isEnabled, state); } void WebPageProxy::unsignedCallback(uint64_t result, uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. // this can validly happen if a load invalidated the callback, though return; } callback->performCallbackWithReturnValue(result); } void WebPageProxy::editingRangeCallback(const EditingRange& range, uint64_t callbackID) { MESSAGE_CHECK(range.isValid()); auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. // this can validly happen if a load invalidated the callback, though return; } callback->performCallbackWithReturnValue(range); } #if PLATFORM(COCOA) void WebPageProxy::machSendRightCallback(const MachSendRight& sendRight, uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) return; callback->performCallbackWithReturnValue(sendRight); } #endif void WebPageProxy::logDiagnosticMessage(const String& message, const String& description, WebCore::ShouldSample shouldSample) { if (!DiagnosticLoggingClient::shouldLogAfterSampling(shouldSample)) return; m_diagnosticLoggingClient->logDiagnosticMessage(this, message, description); } void WebPageProxy::logDiagnosticMessageWithResult(const String& message, const String& description, uint32_t result, WebCore::ShouldSample shouldSample) { if (!DiagnosticLoggingClient::shouldLogAfterSampling(shouldSample)) return; m_diagnosticLoggingClient->logDiagnosticMessageWithResult(this, message, description, static_cast(result)); } void WebPageProxy::logDiagnosticMessageWithValue(const String& message, const String& description, double value, unsigned significantFigures, ShouldSample shouldSample) { if (!DiagnosticLoggingClient::shouldLogAfterSampling(shouldSample)) return; m_diagnosticLoggingClient->logDiagnosticMessageWithValue(this, message, description, String::number(value, significantFigures)); } void WebPageProxy::logDiagnosticMessageWithEnhancedPrivacy(const String& message, const String& description, ShouldSample shouldSample) { if (!DiagnosticLoggingClient::shouldLogAfterSampling(shouldSample)) return; m_diagnosticLoggingClient->logDiagnosticMessageWithEnhancedPrivacy(this, message, description); } void WebPageProxy::rectForCharacterRangeCallback(const IntRect& rect, const EditingRange& actualRange, uint64_t callbackID) { MESSAGE_CHECK(actualRange.isValid()); auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. // this can validly happen if a load invalidated the callback, though return; } callback->performCallbackWithReturnValue(rect, actualRange); } #if PLATFORM(GTK) void WebPageProxy::printFinishedCallback(const ResourceError& printError, uint64_t callbackID) { auto callback = m_callbacks.take(callbackID); if (!callback) { // FIXME: Log error or assert. return; } callback->performCallbackWithReturnValue(API::Error::create(printError).ptr()); } #endif void WebPageProxy::focusedFrameChanged(uint64_t frameID) { if (!frameID) { m_focusedFrame = nullptr; return; } WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); m_focusedFrame = frame; } void WebPageProxy::frameSetLargestFrameChanged(uint64_t frameID) { if (!frameID) { m_frameSetLargestFrame = nullptr; return; } WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); m_frameSetLargestFrame = frame; } void WebPageProxy::processDidBecomeUnresponsive() { if (!isValid()) return; updateBackingStoreDiscardableState(); if (m_navigationClient) m_navigationClient->processDidBecomeUnresponsive(*this); else m_loaderClient->processDidBecomeUnresponsive(*this); } void WebPageProxy::processDidBecomeResponsive() { if (!isValid()) return; updateBackingStoreDiscardableState(); if (m_navigationClient) m_navigationClient->processDidBecomeResponsive(*this); else m_loaderClient->processDidBecomeResponsive(*this); } void WebPageProxy::willChangeProcessIsResponsive() { m_pageLoadState.willChangeProcessIsResponsive(); } void WebPageProxy::didChangeProcessIsResponsive() { m_pageLoadState.didChangeProcessIsResponsive(); } void WebPageProxy::processDidCrash() { ASSERT(m_isValid); // There is a nested transaction in resetStateAfterProcessExited() that we don't want to commit before the client call. PageLoadState::Transaction transaction = m_pageLoadState.transaction(); resetStateAfterProcessExited(); navigationState().clearAllNavigations(); if (m_navigationClient) m_navigationClient->processDidCrash(*this); else m_loaderClient->processDidCrash(*this); if (m_controlledByAutomation) { if (auto* automationSession = process().processPool().automationSession()) automationSession->terminate(); } } #if PLATFORM(IOS) void WebPageProxy::processWillBecomeSuspended() { if (!isValid()) return; m_hasNetworkRequestsOnSuspended = m_pageLoadState.networkRequestsInProgress(); if (m_hasNetworkRequestsOnSuspended) setNetworkRequestsInProgress(false); } void WebPageProxy::processWillBecomeForeground() { if (!isValid()) return; if (m_hasNetworkRequestsOnSuspended) { setNetworkRequestsInProgress(true); m_hasNetworkRequestsOnSuspended = false; } } #endif void WebPageProxy::resetState(ResetStateReason resetStateReason) { m_mainFrame = nullptr; #if PLATFORM(COCOA) m_scrollingPerformanceData = nullptr; #endif m_drawingArea = nullptr; hideValidationMessage(); if (m_inspector) { m_inspector->invalidate(); m_inspector = nullptr; } #if ENABLE(FULLSCREEN_API) if (m_fullScreenManager) { m_fullScreenManager->invalidate(); m_fullScreenManager = nullptr; } #endif #if ENABLE(VIBRATION) m_vibration->invalidate(); #endif if (m_openPanelResultListener) { m_openPanelResultListener->invalidate(); m_openPanelResultListener = nullptr; } #if ENABLE(TOUCH_EVENTS) m_touchEventTracking.reset(); #endif #if ENABLE(INPUT_TYPE_COLOR) if (m_colorPicker) { m_colorPicker->invalidate(); m_colorPicker = nullptr; } #endif #if ENABLE(GEOLOCATION) m_geolocationPermissionRequestManager.invalidateRequests(); #endif #if ENABLE(MEDIA_STREAM) m_userMediaPermissionRequestManager = nullptr; #endif m_notificationPermissionRequestManager.invalidateRequests(); m_toolTip = String(); m_mainFrameHasHorizontalScrollbar = false; m_mainFrameHasVerticalScrollbar = false; m_mainFrameIsPinnedToLeftSide = true; m_mainFrameIsPinnedToRightSide = true; m_mainFrameIsPinnedToTopSide = true; m_mainFrameIsPinnedToBottomSide = true; m_visibleScrollerThumbRect = IntRect(); #if (PLATFORM(IOS) && HAVE(AVKIT)) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) if (m_playbackSessionManager) { m_playbackSessionManager->invalidate(); m_playbackSessionManager = nullptr; } if (m_videoFullscreenManager) { m_videoFullscreenManager->invalidate(); m_videoFullscreenManager = nullptr; } #endif #if PLATFORM(IOS) m_firstLayerTreeTransactionIdAfterDidCommitLoad = 0; m_lastVisibleContentRectUpdate = VisibleContentRectUpdateInfo(); m_dynamicViewportSizeUpdateWaitingForTarget = false; m_dynamicViewportSizeUpdateWaitingForLayerTreeCommit = false; m_dynamicViewportSizeUpdateLayerTreeTransactionID = 0; m_layerTreeTransactionIdAtLastTouchStart = 0; m_hasNetworkRequestsOnSuspended = false; m_isKeyboardAnimatingIn = false; m_isScrollingOrZooming = false; #endif #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS) m_pageClient.mediaSessionManager().removeAllPlaybackTargetPickerClients(*this); #endif #if ENABLE(APPLE_PAY) m_paymentCoordinator = nullptr; #endif CallbackBase::Error error; switch (resetStateReason) { case ResetStateReason::PageInvalidated: error = CallbackBase::Error::OwnerWasInvalidated; break; case ResetStateReason::WebProcessExited: error = CallbackBase::Error::ProcessExited; break; } m_callbacks.invalidate(error); m_loadDependentStringCallbackIDs.clear(); Vector editCommandVector; copyToVector(m_editCommandSet, editCommandVector); m_editCommandSet.clear(); for (size_t i = 0, size = editCommandVector.size(); i < size; ++i) editCommandVector[i]->invalidate(); m_activePopupMenu = nullptr; m_mediaState = MediaProducer::IsNotPlaying; #if ENABLE(POINTER_LOCK) requestPointerUnlock(); #endif } void WebPageProxy::resetStateAfterProcessExited() { if (!isValid()) return; // FIXME: It's weird that resetStateAfterProcessExited() is called even though the process is launching. ASSERT(m_process->state() == WebProcessProxy::State::Launching || m_process->state() == WebProcessProxy::State::Terminated); #if PLATFORM(IOS) m_activityToken = nullptr; #endif m_pageIsUserObservableCount = nullptr; m_visiblePageToken = nullptr; m_isValid = false; m_isPageSuspended = false; m_needsToFinishInitializingWebPageAfterProcessLaunch = false; m_editorState = EditorState(); m_pageClient.processDidExit(); resetState(ResetStateReason::WebProcessExited); m_pageClient.clearAllEditCommands(); m_pendingLearnOrIgnoreWordMessageCount = 0; // Can't expect DidReceiveEvent notifications from a crashed web process. m_keyEventQueue.clear(); m_wheelEventQueue.clear(); m_currentlyProcessedWheelEvents.clear(); m_nextMouseMoveEvent = nullptr; m_currentlyProcessedMouseDownEvent = nullptr; m_processingMouseMoveEvent = false; #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) m_touchEventQueue.clear(); #endif #if PLATFORM(MAC) m_pageClient.dismissContentRelativeChildWindows(); #endif PageLoadState::Transaction transaction = m_pageLoadState.transaction(); m_pageLoadState.reset(transaction); m_process->responsivenessTimer().processTerminated(); } WebPageCreationParameters WebPageProxy::creationParameters() { WebPageCreationParameters parameters; parameters.viewSize = m_pageClient.viewSize(); parameters.activityState = m_activityState; parameters.drawingAreaType = m_drawingArea->type(); parameters.store = preferencesStore(); parameters.pageGroupData = m_pageGroup->data(); parameters.drawsBackground = m_drawsBackground; parameters.isEditable = m_isEditable; parameters.underlayColor = m_underlayColor; parameters.useFixedLayout = m_useFixedLayout; parameters.fixedLayoutSize = m_fixedLayoutSize; parameters.suppressScrollbarAnimations = m_suppressScrollbarAnimations; parameters.paginationMode = m_paginationMode; parameters.paginationBehavesLikeColumns = m_paginationBehavesLikeColumns; parameters.pageLength = m_pageLength; parameters.gapBetweenPages = m_gapBetweenPages; parameters.paginationLineGridEnabled = m_paginationLineGridEnabled; parameters.userAgent = userAgent(); parameters.itemStates = m_backForwardList->itemStates(); parameters.sessionID = m_sessionID; parameters.highestUsedBackForwardItemID = WebBackForwardListItem::highestUsedItemID(); parameters.userContentControllerID = m_userContentController->identifier(); parameters.visitedLinkTableID = m_visitedLinkStore->identifier(); parameters.websiteDataStoreID = m_websiteDataStore->identifier(); parameters.canRunBeforeUnloadConfirmPanel = m_uiClient->canRunBeforeUnloadConfirmPanel(); parameters.canRunModal = m_canRunModal; parameters.deviceScaleFactor = deviceScaleFactor(); parameters.viewScaleFactor = m_viewScaleFactor; parameters.topContentInset = m_topContentInset; parameters.mediaVolume = m_mediaVolume; parameters.muted = m_mutedState; parameters.mayStartMediaWhenInWindow = m_mayStartMediaWhenInWindow; parameters.minimumLayoutSize = m_minimumLayoutSize; parameters.autoSizingShouldExpandToViewHeight = m_autoSizingShouldExpandToViewHeight; parameters.scrollPinningBehavior = m_scrollPinningBehavior; if (m_scrollbarOverlayStyle) parameters.scrollbarOverlayStyle = m_scrollbarOverlayStyle.value(); else parameters.scrollbarOverlayStyle = std::nullopt; parameters.backgroundExtendsBeyondPage = m_backgroundExtendsBeyondPage; parameters.layerHostingMode = m_layerHostingMode; parameters.controlledByAutomation = m_controlledByAutomation; #if ENABLE(REMOTE_INSPECTOR) parameters.allowsRemoteInspection = m_allowsRemoteInspection; parameters.remoteInspectionNameOverride = m_remoteInspectionNameOverride; #endif #if PLATFORM(MAC) parameters.colorSpace = m_pageClient.colorSpace(); #endif #if PLATFORM(IOS) parameters.screenSize = screenSize(); parameters.availableScreenSize = availableScreenSize(); parameters.textAutosizingWidth = textAutosizingWidth(); parameters.mimeTypesWithCustomContentProviders = m_pageClient.mimeTypesWithCustomContentProviders(); parameters.ignoresViewportScaleLimits = m_forceAlwaysUserScalable; #endif #if PLATFORM(MAC) parameters.appleMailPaginationQuirkEnabled = appleMailPaginationQuirkEnabled(); #else parameters.appleMailPaginationQuirkEnabled = false; #endif #if PLATFORM(COCOA) parameters.smartInsertDeleteEnabled = m_isSmartInsertDeleteEnabled; #endif parameters.shouldScaleViewToFitDocument = m_shouldScaleViewToFitDocument; parameters.userInterfaceLayoutDirection = m_pageClient.userInterfaceLayoutDirection(); parameters.observedLayoutMilestones = m_observedLayoutMilestones; parameters.overrideContentSecurityPolicy = m_overrideContentSecurityPolicy; return parameters; } void WebPageProxy::enterAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext) { m_pageClient.enterAcceleratedCompositingMode(layerTreeContext); } void WebPageProxy::exitAcceleratedCompositingMode() { m_pageClient.exitAcceleratedCompositingMode(); } void WebPageProxy::updateAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext) { m_pageClient.updateAcceleratedCompositingMode(layerTreeContext); } void WebPageProxy::backForwardClear() { m_backForwardList->clear(); } #if ENABLE(GAMEPAD) void WebPageProxy::gamepadActivity(const Vector& gamepadDatas, bool shouldMakeGamepadsVisible) { m_process->send(Messages::WebPage::GamepadActivity(gamepadDatas, shouldMakeGamepadsVisible), m_pageID); } #endif void WebPageProxy::canAuthenticateAgainstProtectionSpace(uint64_t loaderID, uint64_t frameID, const ProtectionSpace& coreProtectionSpace) { #if USE(PROTECTION_SPACE_AUTH_CALLBACK) WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); RefPtr protectionSpace = WebProtectionSpace::create(coreProtectionSpace); bool canAuthenticate; if (m_navigationClient) canAuthenticate = m_navigationClient->canAuthenticateAgainstProtectionSpace(*this, protectionSpace.get()); else canAuthenticate = m_loaderClient->canAuthenticateAgainstProtectionSpaceInFrame(*this, *frame, protectionSpace.get()); m_process->processPool().sendToNetworkingProcess(Messages::NetworkProcess::ContinueCanAuthenticateAgainstProtectionSpace(loaderID, canAuthenticate)); #endif } void WebPageProxy::didReceiveAuthenticationChallenge(uint64_t frameID, const AuthenticationChallenge& coreChallenge, uint64_t challengeID) { didReceiveAuthenticationChallengeProxy(frameID, AuthenticationChallengeProxy::create(coreChallenge, challengeID, m_process->connection())); } void WebPageProxy::didReceiveAuthenticationChallengeProxy(uint64_t frameID, PassRefPtr prpAuthenticationChallenge) { ASSERT(prpAuthenticationChallenge); WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); RefPtr authenticationChallenge = prpAuthenticationChallenge; if (m_navigationClient) m_navigationClient->didReceiveAuthenticationChallenge(*this, authenticationChallenge.get()); else m_loaderClient->didReceiveAuthenticationChallengeInFrame(*this, *frame, authenticationChallenge.get()); } void WebPageProxy::exceededDatabaseQuota(uint64_t frameID, const String& originIdentifier, const String& databaseName, const String& displayName, uint64_t currentQuota, uint64_t currentOriginUsage, uint64_t currentDatabaseUsage, uint64_t expectedUsage, PassRefPtr reply) { ExceededDatabaseQuotaRecords& records = ExceededDatabaseQuotaRecords::singleton(); std::unique_ptr newRecord = records.createRecord(frameID, originIdentifier, databaseName, displayName, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage, reply); records.add(WTFMove(newRecord)); if (records.areBeingProcessed()) return; ExceededDatabaseQuotaRecords::Record* record = records.next(); while (record) { WebFrameProxy* frame = m_process->webFrame(record->frameID); MESSAGE_CHECK(frame); RefPtr origin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(record->originIdentifier)->securityOrigin()); auto currentReply = record->reply; m_uiClient->exceededDatabaseQuota(this, frame, origin.get(), record->databaseName, record->displayName, record->currentQuota, record->currentOriginUsage, record->currentDatabaseUsage, record->expectedUsage, [currentReply](unsigned long long newQuota) { currentReply->send(newQuota); }); record = records.next(); } } void WebPageProxy::reachedApplicationCacheOriginQuota(const String& originIdentifier, uint64_t currentQuota, uint64_t totalBytesNeeded, PassRefPtr reply) { Ref securityOrigin = SecurityOriginData::fromDatabaseIdentifier(originIdentifier)->securityOrigin(); m_uiClient->reachedApplicationCacheOriginQuota(this, securityOrigin.get(), currentQuota, totalBytesNeeded, [reply](unsigned long long newQuota) { reply->send(newQuota); }); } void WebPageProxy::requestGeolocationPermissionForFrame(uint64_t geolocationID, uint64_t frameID, String originIdentifier) { WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); // FIXME: Geolocation should probably be using toString() as its string representation instead of databaseIdentifier(). RefPtr origin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(originIdentifier)->securityOrigin()); RefPtr request = m_geolocationPermissionRequestManager.createRequest(geolocationID); if (m_uiClient->decidePolicyForGeolocationPermissionRequest(this, frame, origin.get(), request.get())) return; if (m_pageClient.decidePolicyForGeolocationPermissionRequest(*frame, *origin, *request)) return; request->deny(); } #if ENABLE(MEDIA_STREAM) UserMediaPermissionRequestManagerProxy& WebPageProxy::userMediaPermissionRequestManager() { if (m_userMediaPermissionRequestManager) return *m_userMediaPermissionRequestManager; m_userMediaPermissionRequestManager = std::make_unique(*this); return *m_userMediaPermissionRequestManager; } #endif void WebPageProxy::requestUserMediaPermissionForFrame(uint64_t userMediaID, uint64_t frameID, String userMediaDocumentOriginIdentifier, String topLevelDocumentOriginIdentifier, const WebCore::MediaConstraintsData& audioConstraintsData, const WebCore::MediaConstraintsData& videoConstraintsData) { #if ENABLE(MEDIA_STREAM) MESSAGE_CHECK(m_process->webFrame(frameID)); userMediaPermissionRequestManager().requestUserMediaPermissionForFrame(userMediaID, frameID, userMediaDocumentOriginIdentifier, topLevelDocumentOriginIdentifier, audioConstraintsData, videoConstraintsData); #else UNUSED_PARAM(userMediaID); UNUSED_PARAM(frameID); UNUSED_PARAM(userMediaDocumentOriginIdentifier); UNUSED_PARAM(topLevelDocumentOriginIdentifier); UNUSED_PARAM(audioConstraintsData); UNUSED_PARAM(videoConstraintsData); #endif } void WebPageProxy::enumerateMediaDevicesForFrame(uint64_t userMediaID, uint64_t frameID, String userMediaDocumentOriginIdentifier, String topLevelDocumentOriginIdentifier) { #if ENABLE(MEDIA_STREAM) WebFrameProxy* frame = m_process->webFrame(frameID); MESSAGE_CHECK(frame); userMediaPermissionRequestManager().enumerateMediaDevicesForFrame(userMediaID, frameID, userMediaDocumentOriginIdentifier, topLevelDocumentOriginIdentifier); #else UNUSED_PARAM(userMediaID); UNUSED_PARAM(frameID); UNUSED_PARAM(userMediaDocumentOriginIdentifier); UNUSED_PARAM(topLevelDocumentOriginIdentifier); #endif } void WebPageProxy::clearUserMediaState() { #if ENABLE(MEDIA_STREAM) userMediaPermissionRequestManager().clearCachedState(); #endif } void WebPageProxy::requestNotificationPermission(uint64_t requestID, const String& originString) { if (!isRequestIDValid(requestID)) return; RefPtr origin = API::SecurityOrigin::createFromString(originString); RefPtr request = m_notificationPermissionRequestManager.createRequest(requestID); if (!m_uiClient->decidePolicyForNotificationPermissionRequest(this, origin.get(), request.get())) request->deny(); } void WebPageProxy::showNotification(const String& title, const String& body, const String& iconURL, const String& tag, const String& lang, const String& dir, const String& originString, uint64_t notificationID) { m_process->processPool().supplement()->show(this, title, body, iconURL, tag, lang, dir, originString, notificationID); } void WebPageProxy::cancelNotification(uint64_t notificationID) { m_process->processPool().supplement()->cancel(this, notificationID); } void WebPageProxy::clearNotifications(const Vector& notificationIDs) { m_process->processPool().supplement()->clearNotifications(this, notificationIDs); } void WebPageProxy::didDestroyNotification(uint64_t notificationID) { m_process->processPool().supplement()->didDestroyNotification(this, notificationID); } float WebPageProxy::headerHeight(WebFrameProxy* frame) { if (frame->isDisplayingPDFDocument()) return 0; return m_uiClient->headerHeight(this, frame); } float WebPageProxy::footerHeight(WebFrameProxy* frame) { if (frame->isDisplayingPDFDocument()) return 0; return m_uiClient->footerHeight(this, frame); } void WebPageProxy::drawHeader(WebFrameProxy* frame, const FloatRect& rect) { if (frame->isDisplayingPDFDocument()) return; m_uiClient->drawHeader(this, frame, rect); } void WebPageProxy::drawFooter(WebFrameProxy* frame, const FloatRect& rect) { if (frame->isDisplayingPDFDocument()) return; m_uiClient->drawFooter(this, frame, rect); } void WebPageProxy::runModal() { // Since runModal() can (and probably will) spin a nested run loop we need to turn off the responsiveness timer. m_process->responsivenessTimer().stop(); // Our Connection's run loop might have more messages waiting to be handled after this RunModal message. // To make sure they are handled inside of the the nested modal run loop we must first signal the Connection's // run loop so we're guaranteed that it has a chance to wake up. // See http://webkit.org/b/89590 for more discussion. m_process->connection()->wakeUpRunLoop(); m_uiClient->runModal(this); } void WebPageProxy::notifyScrollerThumbIsVisibleInRect(const IntRect& scrollerThumb) { m_visibleScrollerThumbRect = scrollerThumb; } void WebPageProxy::recommendedScrollbarStyleDidChange(int32_t newStyle) { #if USE(APPKIT) m_pageClient.recommendedScrollbarStyleDidChange(static_cast(newStyle)); #else UNUSED_PARAM(newStyle); #endif } void WebPageProxy::didChangeScrollbarsForMainFrame(bool hasHorizontalScrollbar, bool hasVerticalScrollbar) { m_mainFrameHasHorizontalScrollbar = hasHorizontalScrollbar; m_mainFrameHasVerticalScrollbar = hasVerticalScrollbar; } void WebPageProxy::didChangeScrollOffsetPinningForMainFrame(bool pinnedToLeftSide, bool pinnedToRightSide, bool pinnedToTopSide, bool pinnedToBottomSide) { m_mainFrameIsPinnedToLeftSide = pinnedToLeftSide; m_mainFrameIsPinnedToRightSide = pinnedToRightSide; m_mainFrameIsPinnedToTopSide = pinnedToTopSide; m_mainFrameIsPinnedToBottomSide = pinnedToBottomSide; m_uiClient->pinnedStateDidChange(*this); } void WebPageProxy::didChangePageCount(unsigned pageCount) { m_pageCount = pageCount; } void WebPageProxy::pageExtendedBackgroundColorDidChange(const Color& backgroundColor) { m_pageExtendedBackgroundColor = backgroundColor; } #if ENABLE(NETSCAPE_PLUGIN_API) void WebPageProxy::didFailToInitializePlugin(const String& mimeType, const String& frameURLString, const String& pageURLString) { m_loaderClient->didFailToInitializePlugin(*this, createPluginInformationDictionary(mimeType, frameURLString, pageURLString).ptr()); } void WebPageProxy::didBlockInsecurePluginVersion(const String& mimeType, const String& pluginURLString, const String& frameURLString, const String& pageURLString, bool replacementObscured) { RefPtr pluginInformation; #if PLATFORM(COCOA) && ENABLE(NETSCAPE_PLUGIN_API) String newMimeType = mimeType; PluginModuleInfo plugin = m_process->processPool().pluginInfoStore().findPlugin(newMimeType, URL(URL(), pluginURLString)); pluginInformation = createPluginInformationDictionary(plugin, frameURLString, mimeType, pageURLString, String(), String(), replacementObscured); #else UNUSED_PARAM(mimeType); UNUSED_PARAM(pluginURLString); UNUSED_PARAM(frameURLString); UNUSED_PARAM(pageURLString); UNUSED_PARAM(replacementObscured); #endif m_loaderClient->didBlockInsecurePluginVersion(*this, pluginInformation.get()); } #endif // ENABLE(NETSCAPE_PLUGIN_API) bool WebPageProxy::willHandleHorizontalScrollEvents() const { return !m_canShortCircuitHorizontalWheelEvents; } void WebPageProxy::updateWebsitePolicies(const WebsitePolicies& websitePolicies) { m_process->send(Messages::WebPage::UpdateWebsitePolicies(websitePolicies), m_pageID); } void WebPageProxy::didFinishLoadingDataForCustomContentProvider(const String& suggestedFilename, const IPC::DataReference& dataReference) { m_pageClient.didFinishLoadingDataForCustomContentProvider(suggestedFilename, dataReference); } void WebPageProxy::backForwardRemovedItem(uint64_t itemID) { m_process->removeBackForwardItem(itemID); m_process->send(Messages::WebPage::DidRemoveBackForwardItem(itemID), m_pageID); } void WebPageProxy::setCanRunModal(bool canRunModal) { if (!isValid()) return; // It's only possible to change the state for a WebPage which // already qualifies for running modal child web pages, otherwise // there's no other possibility than not allowing it. m_canRunModal = m_uiClient->canRunModal() && canRunModal; m_process->send(Messages::WebPage::SetCanRunModal(m_canRunModal), m_pageID); } bool WebPageProxy::canRunModal() { return isValid() ? m_canRunModal : false; } void WebPageProxy::beginPrinting(WebFrameProxy* frame, const PrintInfo& printInfo) { if (m_isInPrintingMode) return; m_isInPrintingMode = true; m_process->send(Messages::WebPage::BeginPrinting(frame->frameID(), printInfo), m_pageID, printingSendOptions(m_isPerformingDOMPrintOperation)); } void WebPageProxy::endPrinting() { if (!m_isInPrintingMode) return; m_isInPrintingMode = false; m_process->send(Messages::WebPage::EndPrinting(), m_pageID, printingSendOptions(m_isPerformingDOMPrintOperation)); } void WebPageProxy::computePagesForPrinting(WebFrameProxy* frame, const PrintInfo& printInfo, PassRefPtr prpCallback) { RefPtr callback = prpCallback; if (!isValid()) { callback->invalidate(); return; } uint64_t callbackID = callback->callbackID(); m_callbacks.put(callback); m_isInPrintingMode = true; m_process->send(Messages::WebPage::ComputePagesForPrinting(frame->frameID(), printInfo, callbackID), m_pageID, printingSendOptions(m_isPerformingDOMPrintOperation)); } #if PLATFORM(COCOA) void WebPageProxy::drawRectToImage(WebFrameProxy* frame, const PrintInfo& printInfo, const IntRect& rect, const WebCore::IntSize& imageSize, PassRefPtr prpCallback) { RefPtr callback = prpCallback; if (!isValid()) { callback->invalidate(); return; } uint64_t callbackID = callback->callbackID(); m_callbacks.put(callback); m_process->send(Messages::WebPage::DrawRectToImage(frame->frameID(), printInfo, rect, imageSize, callbackID), m_pageID, printingSendOptions(m_isPerformingDOMPrintOperation)); } void WebPageProxy::drawPagesToPDF(WebFrameProxy* frame, const PrintInfo& printInfo, uint32_t first, uint32_t count, PassRefPtr prpCallback) { RefPtr callback = prpCallback; if (!isValid()) { callback->invalidate(); return; } uint64_t callbackID = callback->callbackID(); m_callbacks.put(callback); m_process->send(Messages::WebPage::DrawPagesToPDF(frame->frameID(), printInfo, first, count, callbackID), m_pageID, printingSendOptions(m_isPerformingDOMPrintOperation)); } #elif PLATFORM(GTK) void WebPageProxy::drawPagesForPrinting(WebFrameProxy* frame, const PrintInfo& printInfo, PassRefPtr didPrintCallback) { RefPtr callback = didPrintCallback; if (!isValid()) { callback->invalidate(); return; } uint64_t callbackID = callback->callbackID(); m_callbacks.put(callback); m_isInPrintingMode = true; m_process->send(Messages::WebPage::DrawPagesForPrinting(frame->frameID(), printInfo, callbackID), m_pageID, printingSendOptions(m_isPerformingDOMPrintOperation)); } #endif void WebPageProxy::updateBackingStoreDiscardableState() { ASSERT(isValid()); bool isDiscardable; if (!m_process->responsivenessTimer().isResponsive()) isDiscardable = false; else isDiscardable = !m_pageClient.isViewWindowActive() || !isViewVisible(); m_drawingArea->setBackingStoreIsDiscardable(isDiscardable); } void WebPageProxy::saveDataToFileInDownloadsFolder(const String& suggestedFilename, const String& mimeType, const String& originatingURLString, API::Data* data) { m_uiClient->saveDataToFileInDownloadsFolder(this, suggestedFilename, mimeType, originatingURLString, data); } void WebPageProxy::savePDFToFileInDownloadsFolder(const String& suggestedFilename, const String& originatingURLString, const IPC::DataReference& dataReference) { if (!suggestedFilename.endsWith(".pdf", false)) return; saveDataToFileInDownloadsFolder(suggestedFilename, "application/pdf", originatingURLString, API::Data::create(dataReference.data(), dataReference.size()).ptr()); } void WebPageProxy::setMinimumLayoutSize(const IntSize& minimumLayoutSize) { if (m_minimumLayoutSize == minimumLayoutSize) return; m_minimumLayoutSize = minimumLayoutSize; if (!isValid()) return; m_process->send(Messages::WebPage::SetMinimumLayoutSize(minimumLayoutSize), m_pageID); m_drawingArea->minimumLayoutSizeDidChange(); #if USE(APPKIT) if (m_minimumLayoutSize.width() <= 0) intrinsicContentSizeDidChange(IntSize(-1, -1)); #endif } void WebPageProxy::setAutoSizingShouldExpandToViewHeight(bool shouldExpand) { if (m_autoSizingShouldExpandToViewHeight == shouldExpand) return; m_autoSizingShouldExpandToViewHeight = shouldExpand; if (!isValid()) return; m_process->send(Messages::WebPage::SetAutoSizingShouldExpandToViewHeight(shouldExpand), m_pageID); } #if USE(AUTOMATIC_TEXT_REPLACEMENT) void WebPageProxy::toggleSmartInsertDelete() { if (TextChecker::isTestingMode()) TextChecker::setSmartInsertDeleteEnabled(!TextChecker::isSmartInsertDeleteEnabled()); } void WebPageProxy::toggleAutomaticQuoteSubstitution() { if (TextChecker::isTestingMode()) TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled); } void WebPageProxy::toggleAutomaticLinkDetection() { if (TextChecker::isTestingMode()) TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled); } void WebPageProxy::toggleAutomaticDashSubstitution() { if (TextChecker::isTestingMode()) TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled); } void WebPageProxy::toggleAutomaticTextReplacement() { if (TextChecker::isTestingMode()) TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled); } #endif #if PLATFORM(MAC) void WebPageProxy::substitutionsPanelIsShowing(bool& isShowing) { isShowing = TextChecker::substitutionsPanelIsShowing(); } void WebPageProxy::showCorrectionPanel(int32_t panelType, const FloatRect& boundingBoxOfReplacedString, const String& replacedString, const String& replacementString, const Vector& alternativeReplacementStrings) { m_pageClient.showCorrectionPanel((AlternativeTextType)panelType, boundingBoxOfReplacedString, replacedString, replacementString, alternativeReplacementStrings); } void WebPageProxy::dismissCorrectionPanel(int32_t reason) { m_pageClient.dismissCorrectionPanel((ReasonForDismissingAlternativeText)reason); } void WebPageProxy::dismissCorrectionPanelSoon(int32_t reason, String& result) { result = m_pageClient.dismissCorrectionPanelSoon((ReasonForDismissingAlternativeText)reason); } void WebPageProxy::recordAutocorrectionResponse(int32_t response, const String& replacedString, const String& replacementString) { m_pageClient.recordAutocorrectionResponse(static_cast(response), replacedString, replacementString); } void WebPageProxy::handleAlternativeTextUIResult(const String& result) { if (!isClosed()) m_process->send(Messages::WebPage::HandleAlternativeTextUIResult(result), m_pageID); } #if USE(DICTATION_ALTERNATIVES) void WebPageProxy::showDictationAlternativeUI(const WebCore::FloatRect& boundingBoxOfDictatedText, uint64_t dictationContext) { m_pageClient.showDictationAlternativeUI(boundingBoxOfDictatedText, dictationContext); } void WebPageProxy::removeDictationAlternatives(uint64_t dictationContext) { m_pageClient.removeDictationAlternatives(dictationContext); } void WebPageProxy::dictationAlternatives(uint64_t dictationContext, Vector& result) { result = m_pageClient.dictationAlternatives(dictationContext); } #endif void WebPageProxy::setEditableElementIsFocused(bool editableElementIsFocused) { m_pageClient.setEditableElementIsFocused(editableElementIsFocused); } #endif // PLATFORM(MAC) #if PLATFORM(COCOA) PassRefPtr WebPageProxy::takeViewSnapshot() { return m_pageClient.takeViewSnapshot(); } #endif #if PLATFORM(GTK) void WebPageProxy::setComposition(const String& text, Vector underlines, uint64_t selectionStart, uint64_t selectionEnd, uint64_t replacementRangeStart, uint64_t replacementRangeEnd) { // FIXME: We need to find out how to proper handle the crashes case. if (!isValid()) return; process().send(Messages::WebPage::SetComposition(text, underlines, selectionStart, selectionEnd, replacementRangeStart, replacementRangeEnd), m_pageID); } void WebPageProxy::confirmComposition(const String& compositionString, int64_t selectionStart, int64_t selectionLength) { if (!isValid()) return; process().send(Messages::WebPage::ConfirmComposition(compositionString, selectionStart, selectionLength), m_pageID); } void WebPageProxy::cancelComposition() { if (!isValid()) return; process().send(Messages::WebPage::CancelComposition(), m_pageID); } #endif // PLATFORM(GTK) void WebPageProxy::didSaveToPageCache() { m_process->didSaveToPageCache(); } void WebPageProxy::setScrollPinningBehavior(ScrollPinningBehavior pinning) { if (m_scrollPinningBehavior == pinning) return; m_scrollPinningBehavior = pinning; if (isValid()) m_process->send(Messages::WebPage::SetScrollPinningBehavior(pinning), m_pageID); } void WebPageProxy::setOverlayScrollbarStyle(std::optional scrollbarStyle) { if (!m_scrollbarOverlayStyle && !scrollbarStyle) return; if ((m_scrollbarOverlayStyle && scrollbarStyle) && m_scrollbarOverlayStyle.value() == scrollbarStyle.value()) return; m_scrollbarOverlayStyle = scrollbarStyle; std::optional scrollbarStyleForMessage; if (scrollbarStyle) scrollbarStyleForMessage = static_cast(scrollbarStyle.value()); if (isValid()) m_process->send(Messages::WebPage::SetScrollbarOverlayStyle(scrollbarStyleForMessage), m_pageID); } #if ENABLE(SUBTLE_CRYPTO) void WebPageProxy::wrapCryptoKey(const Vector& key, bool& succeeded, Vector& wrappedKey) { PageClientProtector protector(m_pageClient); Vector masterKey; if (m_navigationClient) { if (RefPtr keyData = m_navigationClient->webCryptoMasterKey(*this)) masterKey = keyData->dataReference().vector(); } else if (!getDefaultWebCryptoMasterKey(masterKey)) { succeeded = false; return; } succeeded = wrapSerializedCryptoKey(masterKey, key, wrappedKey); } void WebPageProxy::unwrapCryptoKey(const Vector& wrappedKey, bool& succeeded, Vector& key) { PageClientProtector protector(m_pageClient); Vector masterKey; if (m_navigationClient) { if (RefPtr keyData = m_navigationClient->webCryptoMasterKey(*this)) masterKey = keyData->dataReference().vector(); } else if (!getDefaultWebCryptoMasterKey(masterKey)) { succeeded = false; return; } succeeded = unwrapSerializedCryptoKey(masterKey, wrappedKey, key); } #endif void WebPageProxy::addMIMETypeWithCustomContentProvider(const String& mimeType) { m_process->send(Messages::WebPage::AddMIMETypeWithCustomContentProvider(mimeType), m_pageID); } #if PLATFORM(COCOA) void WebPageProxy::insertTextAsync(const String& text, const EditingRange& replacementRange, bool registerUndoGroup, EditingRangeIsRelativeTo editingRangeIsRelativeTo, bool suppressSelectionUpdate) { if (!isValid()) return; process().send(Messages::WebPage::InsertTextAsync(text, replacementRange, registerUndoGroup, static_cast(editingRangeIsRelativeTo), suppressSelectionUpdate), m_pageID); } void WebPageProxy::getMarkedRangeAsync(std::function callbackFunction) { if (!isValid()) { callbackFunction(EditingRange(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); process().send(Messages::WebPage::GetMarkedRangeAsync(callbackID), m_pageID); } void WebPageProxy::getSelectedRangeAsync(std::function callbackFunction) { if (!isValid()) { callbackFunction(EditingRange(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); process().send(Messages::WebPage::GetSelectedRangeAsync(callbackID), m_pageID); } void WebPageProxy::characterIndexForPointAsync(const WebCore::IntPoint& point, std::function callbackFunction) { if (!isValid()) { callbackFunction(0, CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); process().send(Messages::WebPage::CharacterIndexForPointAsync(point, callbackID), m_pageID); } void WebPageProxy::firstRectForCharacterRangeAsync(const EditingRange& range, std::function callbackFunction) { if (!isValid()) { callbackFunction(WebCore::IntRect(), EditingRange(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); process().send(Messages::WebPage::FirstRectForCharacterRangeAsync(range, callbackID), m_pageID); } void WebPageProxy::setCompositionAsync(const String& text, Vector underlines, const EditingRange& selectionRange, const EditingRange& replacementRange) { if (!isValid()) { // If this fails, we should call -discardMarkedText on input context to notify the input method. // This will happen naturally later, as part of reloading the page. return; } process().send(Messages::WebPage::SetCompositionAsync(text, underlines, selectionRange, replacementRange), m_pageID); } void WebPageProxy::confirmCompositionAsync() { if (!isValid()) return; process().send(Messages::WebPage::ConfirmCompositionAsync(), m_pageID); } void WebPageProxy::setScrollPerformanceDataCollectionEnabled(bool enabled) { if (enabled == m_scrollPerformanceDataCollectionEnabled) return; m_scrollPerformanceDataCollectionEnabled = enabled; if (m_scrollPerformanceDataCollectionEnabled && !m_scrollingPerformanceData) m_scrollingPerformanceData = std::make_unique(downcast(*m_drawingArea)); else if (!m_scrollPerformanceDataCollectionEnabled) m_scrollingPerformanceData = nullptr; } #endif void WebPageProxy::takeSnapshot(IntRect rect, IntSize bitmapSize, SnapshotOptions options, std::function callbackFunction) { if (!isValid()) { callbackFunction(ShareableBitmap::Handle(), CallbackBase::Error::Unknown); return; } uint64_t callbackID = m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()); m_process->send(Messages::WebPage::TakeSnapshot(rect, bitmapSize, options, callbackID), m_pageID); } void WebPageProxy::navigationGestureDidBegin() { PageClientProtector protector(m_pageClient); m_isShowingNavigationGestureSnapshot = true; m_pageClient.navigationGestureDidBegin(); if (m_navigationClient) m_navigationClient->didBeginNavigationGesture(*this); else m_loaderClient->navigationGestureDidBegin(*this); } void WebPageProxy::navigationGestureWillEnd(bool willNavigate, WebBackForwardListItem& item) { PageClientProtector protector(m_pageClient); m_pageClient.navigationGestureWillEnd(willNavigate, item); if (m_navigationClient) m_navigationClient->willEndNavigationGesture(*this, willNavigate, item); else m_loaderClient->navigationGestureWillEnd(*this, willNavigate, item); } void WebPageProxy::navigationGestureDidEnd(bool willNavigate, WebBackForwardListItem& item) { PageClientProtector protector(m_pageClient); m_pageClient.navigationGestureDidEnd(willNavigate, item); if (m_navigationClient) m_navigationClient->didEndNavigationGesture(*this, willNavigate, item); else m_loaderClient->navigationGestureDidEnd(*this, willNavigate, item); } void WebPageProxy::navigationGestureDidEnd() { PageClientProtector protector(m_pageClient); m_pageClient.navigationGestureDidEnd(); } void WebPageProxy::willRecordNavigationSnapshot(WebBackForwardListItem& item) { PageClientProtector protector(m_pageClient); m_pageClient.willRecordNavigationSnapshot(item); } void WebPageProxy::navigationGestureSnapshotWasRemoved() { m_isShowingNavigationGestureSnapshot = false; m_pageClient.didRemoveNavigationGestureSnapshot(); if (m_navigationClient) m_navigationClient->didRemoveNavigationGestureSnapshot(*this); } void WebPageProxy::isPlayingMediaDidChange(MediaProducer::MediaStateFlags state, uint64_t sourceElementID) { #if ENABLE(MEDIA_SESSION) WebMediaSessionFocusManager* focusManager = process().processPool().supplement(); ASSERT(focusManager); focusManager->updatePlaybackAttributesFromMediaState(this, sourceElementID, state); #endif if (state == m_mediaState) return; #if ENABLE(MEDIA_STREAM) WebCore::MediaProducer::MediaStateFlags oldMediaStateHasActiveCapture = m_mediaState & (WebCore::MediaProducer::HasActiveAudioCaptureDevice | WebCore::MediaProducer::HasActiveVideoCaptureDevice); WebCore::MediaProducer::MediaStateFlags newMediaStateHasActiveCapture = state & (WebCore::MediaProducer::HasActiveAudioCaptureDevice | WebCore::MediaProducer::HasActiveVideoCaptureDevice); #endif MediaProducer::MediaStateFlags playingMediaMask = MediaProducer::IsPlayingAudio | MediaProducer::IsPlayingVideo; MediaProducer::MediaStateFlags oldState = m_mediaState; m_mediaState = state; #if ENABLE(MEDIA_STREAM) if (!oldMediaStateHasActiveCapture && newMediaStateHasActiveCapture) { m_uiClient->didBeginCaptureSession(); userMediaPermissionRequestManager().startedCaptureSession(); } else if (oldMediaStateHasActiveCapture && !newMediaStateHasActiveCapture) { m_uiClient->didEndCaptureSession(); userMediaPermissionRequestManager().endedCaptureSession(); } #endif activityStateDidChange(ActivityState::IsAudible); playingMediaMask |= MediaProducer::HasActiveAudioCaptureDevice | MediaProducer::HasActiveVideoCaptureDevice; if ((oldState & playingMediaMask) != (m_mediaState & playingMediaMask)) m_uiClient->isPlayingAudioDidChange(*this); #if PLATFORM(MAC) if ((oldState & MediaProducer::HasAudioOrVideo) != (m_mediaState & MediaProducer::HasAudioOrVideo)) videoControlsManagerDidChange(); #endif } #if PLATFORM(MAC) void WebPageProxy::videoControlsManagerDidChange() { m_pageClient.videoControlsManagerDidChange(); } bool WebPageProxy::hasActiveVideoForControlsManager() const { #if ENABLE(VIDEO_PRESENTATION_MODE) return m_playbackSessionManager && m_playbackSessionManager->controlsManagerInterface(); #else return false; #endif } void WebPageProxy::requestControlledElementID() const { #if ENABLE(VIDEO_PRESENTATION_MODE) if (m_playbackSessionManager) m_playbackSessionManager->requestControlledElementID(); #endif } void WebPageProxy::handleControlledElementIDResponse(const String& identifier) const { m_pageClient.handleControlledElementIDResponse(identifier); } bool WebPageProxy::isPlayingVideoInEnhancedFullscreen() const { #if ENABLE(VIDEO_PRESENTATION_MODE) return m_videoFullscreenManager && m_videoFullscreenManager->isPlayingVideoInEnhancedFullscreen(); #else return false; #endif } #endif #if PLATFORM(COCOA) void WebPageProxy::requestActiveNowPlayingSessionInfo() { m_process->send(Messages::WebPage::RequestActiveNowPlayingSessionInfo(), m_pageID); } void WebPageProxy::handleActiveNowPlayingSessionInfoResponse(bool hasActiveSession, const String& title, double duration, double elapsedTime) const { m_pageClient.handleActiveNowPlayingSessionInfoResponse(hasActiveSession, title, duration, elapsedTime); } #endif #if ENABLE(MEDIA_SESSION) void WebPageProxy::hasMediaSessionWithActiveMediaElementsDidChange(bool state) { m_hasMediaSessionWithActiveMediaElements = state; } void WebPageProxy::mediaSessionMetadataDidChange(const WebCore::MediaSessionMetadata& metadata) { Ref webMetadata = WebMediaSessionMetadata::create(metadata); m_uiClient->mediaSessionMetadataDidChange(*this, webMetadata.ptr()); } void WebPageProxy::focusedContentMediaElementDidChange(uint64_t elementID) { WebMediaSessionFocusManager* focusManager = process().processPool().supplement(); ASSERT(focusManager); focusManager->setFocusedMediaElement(*this, elementID); } #endif void WebPageProxy::didPlayMediaPreventedFromPlayingWithoutUserGesture() { m_uiClient->didPlayMediaPreventedFromPlayingWithoutUserGesture(*this); } #if PLATFORM(MAC) void WebPageProxy::removeNavigationGestureSnapshot() { m_pageClient.removeNavigationGestureSnapshot(); } void WebPageProxy::performImmediateActionHitTestAtLocation(FloatPoint point) { m_process->send(Messages::WebPage::PerformImmediateActionHitTestAtLocation(point), m_pageID); } void WebPageProxy::immediateActionDidUpdate() { m_process->send(Messages::WebPage::ImmediateActionDidUpdate(), m_pageID); } void WebPageProxy::immediateActionDidCancel() { m_process->send(Messages::WebPage::ImmediateActionDidCancel(), m_pageID); } void WebPageProxy::immediateActionDidComplete() { m_process->send(Messages::WebPage::ImmediateActionDidComplete(), m_pageID); } void WebPageProxy::didPerformImmediateActionHitTest(const WebHitTestResultData& result, bool contentPreventsDefault, const UserData& userData) { m_pageClient.didPerformImmediateActionHitTest(result, contentPreventsDefault, m_process->transformHandlesToObjects(userData.object()).get()); } void* WebPageProxy::immediateActionAnimationControllerForHitTestResult(RefPtr hitTestResult, uint64_t type, RefPtr userData) { return m_pageClient.immediateActionAnimationControllerForHitTestResult(hitTestResult, type, userData); } void WebPageProxy::installActivityStateChangeCompletionHandler(void (^completionHandler)()) { if (!isValid()) { completionHandler(); return; } auto copiedCompletionHandler = Block_copy(completionHandler); auto voidCallback = VoidCallback::create([copiedCompletionHandler] (CallbackBase::Error) { copiedCompletionHandler(); Block_release(copiedCompletionHandler); }, m_process->throttler().backgroundActivityToken()); uint64_t callbackID = m_callbacks.put(WTFMove(voidCallback)); m_nextActivityStateChangeCallbacks.append(callbackID); } void WebPageProxy::handleAcceptedCandidate(WebCore::TextCheckingResult acceptedCandidate) { m_process->send(Messages::WebPage::HandleAcceptedCandidate(acceptedCandidate), m_pageID); } void WebPageProxy::didHandleAcceptedCandidate() { m_pageClient.didHandleAcceptedCandidate(); } void WebPageProxy::setHeaderBannerHeightForTesting(int height) { m_process->send(Messages::WebPage::SetHeaderBannerHeightForTesting(height), m_pageID); } void WebPageProxy::setFooterBannerHeightForTesting(int height) { m_process->send(Messages::WebPage::SetFooterBannerHeightForTesting(height), m_pageID); } #endif void WebPageProxy::imageOrMediaDocumentSizeChanged(const WebCore::IntSize& newSize) { m_uiClient->imageOrMediaDocumentSizeChanged(newSize); } void WebPageProxy::setShouldDispatchFakeMouseMoveEvents(bool shouldDispatchFakeMouseMoveEvents) { m_process->send(Messages::WebPage::SetShouldDispatchFakeMouseMoveEvents(shouldDispatchFakeMouseMoveEvents), m_pageID); } void WebPageProxy::handleAutoFillButtonClick(const UserData& userData) { m_uiClient->didClickAutoFillButton(*this, m_process->transformHandlesToObjects(userData.object()).get()); } #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS) void WebPageProxy::addPlaybackTargetPickerClient(uint64_t contextId) { m_pageClient.mediaSessionManager().addPlaybackTargetPickerClient(*this, contextId); } void WebPageProxy::removePlaybackTargetPickerClient(uint64_t contextId) { m_pageClient.mediaSessionManager().removePlaybackTargetPickerClient(*this, contextId); } void WebPageProxy::showPlaybackTargetPicker(uint64_t contextId, const WebCore::FloatRect& rect, bool hasVideo) { m_pageClient.mediaSessionManager().showPlaybackTargetPicker(*this, contextId, m_pageClient.rootViewToScreen(IntRect(rect)), hasVideo); } void WebPageProxy::playbackTargetPickerClientStateDidChange(uint64_t contextId, WebCore::MediaProducer::MediaStateFlags state) { m_pageClient.mediaSessionManager().clientStateDidChange(*this, contextId, state); } void WebPageProxy::setMockMediaPlaybackTargetPickerEnabled(bool enabled) { m_pageClient.mediaSessionManager().setMockMediaPlaybackTargetPickerEnabled(enabled); } void WebPageProxy::setMockMediaPlaybackTargetPickerState(const String& name, WebCore::MediaPlaybackTargetContext::State state) { m_pageClient.mediaSessionManager().setMockMediaPlaybackTargetPickerState(name, state); } void WebPageProxy::setPlaybackTarget(uint64_t contextId, Ref&& target) { if (!isValid()) return; m_process->send(Messages::WebPage::PlaybackTargetSelected(contextId, target->targetContext()), m_pageID); } void WebPageProxy::externalOutputDeviceAvailableDidChange(uint64_t contextId, bool available) { if (!isValid()) return; m_process->send(Messages::WebPage::PlaybackTargetAvailabilityDidChange(contextId, available), m_pageID); } void WebPageProxy::setShouldPlayToPlaybackTarget(uint64_t contextId, bool shouldPlay) { if (!isValid()) return; m_process->send(Messages::WebPage::SetShouldPlayToPlaybackTarget(contextId, shouldPlay), m_pageID); } #endif void WebPageProxy::didChangeBackgroundColor() { m_pageClient.didChangeBackgroundColor(); } void WebPageProxy::clearWheelEventTestTrigger() { if (!isValid()) return; m_process->send(Messages::WebPage::ClearWheelEventTestTrigger(), m_pageID); } void WebPageProxy::callAfterNextPresentationUpdate(std::function callback) { if (!isValid() || !m_drawingArea) { callback(CallbackBase::Error::OwnerWasInvalidated); return; } m_drawingArea->dispatchAfterEnsuringDrawing(callback); } void WebPageProxy::setShouldScaleViewToFitDocument(bool shouldScaleViewToFitDocument) { if (m_shouldScaleViewToFitDocument == shouldScaleViewToFitDocument) return; m_shouldScaleViewToFitDocument = shouldScaleViewToFitDocument; if (!isValid()) return; m_process->send(Messages::WebPage::SetShouldScaleViewToFitDocument(shouldScaleViewToFitDocument), m_pageID); } void WebPageProxy::didRestoreScrollPosition() { m_pageClient.didRestoreScrollPosition(); } void WebPageProxy::getLoadDecisionForIcon(const WebCore::LinkIcon& icon, uint64_t loadIdentifier) { if (!m_iconLoadingClient) return; m_iconLoadingClient->getLoadDecisionForIcon(icon, [this, protectedThis = RefPtr(this), loadIdentifier](std::function callbackFunction) { if (!isValid()) { if (callbackFunction) callbackFunction(nullptr, CallbackBase::Error::Unknown); return; } bool decision = (bool)callbackFunction; uint64_t newCallbackIdentifier = decision ? m_callbacks.put(WTFMove(callbackFunction), m_process->throttler().backgroundActivityToken()) : 0; m_process->send(Messages::WebPage::DidGetLoadDecisionForIcon(decision, loadIdentifier, newCallbackIdentifier), m_pageID); }); } void WebPageProxy::finishedLoadingIcon(uint64_t callbackIdentifier, const IPC::DataReference& data) { dataCallback(data, callbackIdentifier); } void WebPageProxy::setResourceCachingDisabled(bool disabled) { if (m_isResourceCachingDisabled == disabled) return; m_isResourceCachingDisabled = disabled; if (!isValid()) return; m_process->send(Messages::WebPage::SetResourceCachingDisabled(disabled), m_pageID); } WebCore::UserInterfaceLayoutDirection WebPageProxy::userInterfaceLayoutDirection() { return m_pageClient.userInterfaceLayoutDirection(); } void WebPageProxy::setUserInterfaceLayoutDirection(WebCore::UserInterfaceLayoutDirection userInterfaceLayoutDirection) { if (!isValid()) return; m_process->send(Messages::WebPage::SetUserInterfaceLayoutDirection(static_cast(userInterfaceLayoutDirection)), m_pageID); } void WebPageProxy::hideValidationMessage() { #if PLATFORM(COCOA) m_validationBubble = nullptr; #endif } #if ENABLE(POINTER_LOCK) void WebPageProxy::requestPointerLock() { ASSERT(!m_isPointerLockPending); ASSERT(!m_isPointerLocked); m_isPointerLockPending = true; if (!isViewVisible() || !(m_activityState & ActivityState::IsFocused)) { didDenyPointerLock(); return; } m_uiClient->requestPointerLock(this); } void WebPageProxy::didAllowPointerLock() { ASSERT(m_isPointerLockPending && !m_isPointerLocked); m_isPointerLocked = true; m_isPointerLockPending = false; #if PLATFORM(MAC) CGDisplayHideCursor(CGMainDisplayID()); CGAssociateMouseAndMouseCursorPosition(false); #endif m_process->send(Messages::WebPage::DidAcquirePointerLock(), m_pageID); } void WebPageProxy::didDenyPointerLock() { ASSERT(m_isPointerLockPending && !m_isPointerLocked); m_isPointerLockPending = false; m_process->send(Messages::WebPage::DidNotAcquirePointerLock(), m_pageID); } void WebPageProxy::requestPointerUnlock() { if (m_isPointerLocked) { #if PLATFORM(MAC) CGAssociateMouseAndMouseCursorPosition(true); CGDisplayShowCursor(CGMainDisplayID()); #endif m_uiClient->didLosePointerLock(this); m_process->send(Messages::WebPage::DidLosePointerLock(), m_pageID); } if (m_isPointerLockPending) { m_uiClient->didLosePointerLock(this); m_process->send(Messages::WebPage::DidNotAcquirePointerLock(), m_pageID); } m_isPointerLocked = false; m_isPointerLockPending = false; } #endif } // namespace WebKit