blob: a428dd118c5ad552f4ce95614929efdf1b825935 [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
* Copyright (C) 2013-2026 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "Internals.h"
#include "AXObjectCacheInlines.h"
#include "AddEventListenerOptionsInlines.h"
#include "AnimationTimeline.h"
#include "AnimationTimelinesController.h"
#include "AudioSession.h"
#include "AudioTrackPrivateMediaStream.h"
#include "Autofill.h"
#include "BackForwardCache.h"
#include "BackForwardController.h"
#include "BitmapImage.h"
#include "Blob.h"
#include "BoundaryPointInlines.h"
#include "CSSKeyframesRule.h"
#include "CSSMediaRule.h"
#include "CSSPropertyParser.h"
#include "CSSPropertyParserConsumer+ColorInlines.h"
#include "CSSSelectorParser.h"
#include "CSSStyleRule.h"
#include "CSSSupportsRule.h"
#include "CacheStorageConnection.h"
#include "CacheStorageProvider.h"
#include "CachedImage.h"
#include "CanvasBase.h"
#include "CertificateInfo.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "ClientOrigin.h"
#include "ColorChooser.h"
#include "ColorSerialization.h"
#include "ComposedTreeIterator.h"
#include "ContainerNodeInlines.h"
#include "ContextDestructionObserverInlines.h"
#include "CookieJar.h"
#include "CrossOriginPreflightResultCache.h"
#include "Cursor.h"
#include "DOMAsyncIterator.h"
#include "DOMPointReadOnly.h"
#include "DOMRect.h"
#include "DOMRectList.h"
#include "DOMStringList.h"
#include "DOMURL.h"
#include "DeprecatedGlobalSettings.h"
#include "DiagnosticLoggingClient.h"
#include "DisabledAdaptations.h"
#include "DisplayList.h"
#include "DocumentFullscreen.h"
#include "DocumentInlines.h"
#include "DocumentLoader.h"
#include "DocumentMarkerController.h"
#include "DocumentMarkers.h"
#include "DocumentPage.h"
#include "DocumentQuirks.h"
#include "DocumentResourceLoader.h"
#include "DocumentTimeline.h"
#include "DocumentView.h"
#include "Editor.h"
#include "Element.h"
#include "ElementRareData.h"
#include "EventHandler.h"
#include "EventListener.h"
#include "EventLoop.h"
#include "EventNames.h"
#include "EventTargetForTesting.h"
#include "EventTargetInlines.h"
#include "ExtendableEvent.h"
#include "ExtensionStyleSheets.h"
#include "FetchRequest.h"
#include "FetchResponse.h"
#include "File.h"
#include "FileSystemHandle.h"
#include "FloatQuad.h"
#include "FontCache.h"
#include "FormController.h"
#include "FragmentDirectiveGenerator.h"
#include "FrameInspectorController.h"
#include "FrameLoader.h"
#include "FrameMemoryMonitor.h"
#include "FrameSnapshotting.h"
#include "GCObservation.h"
#include "GraphicsLayer.h"
#include "HEVCUtilities.h"
#include "HTMLAnchorElement.h"
#include "HTMLAttachmentElement.h"
#include "HTMLCanvasElement.h"
#include "HTMLIFrameElement.h"
#include "HTMLImageElement.h"
#include "HTMLInputElement.h"
#include "HTMLLinkElement.h"
#include "HTMLNames.h"
#include "HTMLPictureElement.h"
#include "HTMLPlugInElement.h"
#include "HTMLPreloadScanner.h"
#include "HTMLSelectElement.h"
#include "HTMLTextAreaElement.h"
#include "HTMLVideoElement.h"
#include "HighlightRegistry.h"
#include "History.h"
#include "HistoryController.h"
#include "HistoryItem.h"
#include "HitTestResult.h"
#include "IDBRequest.h"
#include "IDBTransaction.h"
#include "ImageData.h"
#include "ImageOverlay.h"
#include "ImageOverlayController.h"
#include "InlineIteratorLineBox.h"
#include "InspectorBackendClient.h"
#include "InspectorDebuggableType.h"
#include "InspectorFrontendClientLocal.h"
#include "InspectorOverlay.h"
#include "InstrumentingAgents.h"
#include "IntRect.h"
#include "InternalSettings.h"
#include "InternalsMapLike.h"
#include "InternalsSetLike.h"
#include "JSDOMPromiseDeferred.h"
#include "JSFile.h"
#include "JSInternals.h"
#include "LegacySchemeRegistry.h"
#include "LoaderStrategy.h"
#include "LocalDOMWindow.h"
#include "LocalFrame.h"
#include "LocalFrameView.h"
#include "LocalizedStrings.h"
#include "Location.h"
#include "MallocStatistics.h"
#include "MediaControlsHost.h"
#include "MediaDevices.h"
#include "MediaKeySession.h"
#include "MediaKeys.h"
#include "MediaMetadata.h"
#include "MediaPlayer.h"
#include "MediaProducer.h"
#include "MediaResourceLoader.h"
#include "MediaSession.h"
#include "MediaSessionActionDetails.h"
#include "MediaStrategy.h"
#include "MediaStreamTrack.h"
#include "MediaUsageInfo.h"
#include "MemoryCache.h"
#include "MemoryInfo.h"
#include "MessagePort.h"
#include "MockAudioDestinationCocoa.h"
#include "MockLibWebRTCPeerConnection.h"
#include "MockPageOverlay.h"
#include "MockPageOverlayClient.h"
#include "Navigation.h"
#include "NavigatorBeacon.h"
#include "NavigatorMediaDevices.h"
#include "NetworkLoadInformation.h"
#include "NodeInlines.h"
#include "Page.h"
#include "PageInspectorController.h"
#include "PageOverlay.h"
#include "PathUtilities.h"
#include "PictureInPictureSupport.h"
#include "PlatformKeyboardEvent.h"
#include "PlatformMediaEngineConfigurationFactory.h"
#include "PlatformMediaSession.h"
#include "PlatformMediaSessionManager.h"
#include "PlatformScreen.h"
#include "PlatformStrategies.h"
#include "PluginData.h"
#include "PluginViewBase.h"
#include "PrintContext.h"
#include "PseudoElement.h"
#include "PushSubscription.h"
#include "PushSubscriptionData.h"
#include "RTCController.h"
#include "RTCNetworkManager.h"
#include "RTCRtpSFrameTransform.h"
#include "Range.h"
#include "ReadableStream.h"
#include "RenderEmbeddedObject.h"
#include "RenderLayerBacking.h"
#include "RenderLayerCompositor.h"
#include "RenderLayerScrollableArea.h"
#include "RenderListBox.h"
#include "RenderObjectInlines.h"
#include "RenderSearchField.h"
#include "RenderTheme.h"
#include "RenderTreeAsText.h"
#include "RenderView.h"
#include "RenderedDocumentMarker.h"
#include "ResolvedStyle.h"
#include "ResourceLoadObserver.h"
#include "SMILTimeContainer.h"
#include "SVGDocumentExtensions.h"
#include "SVGPathStringBuilder.h"
#include "SVGSVGElement.h"
#include "SWClientConnection.h"
#include "ScriptController.h"
#include "ScriptExecutionContextInlines.h"
#include "ScriptedAnimationController.h"
#include "ScrollTimeline.h"
#include "ScrollToOptions.h"
#include "ScrollbarsControllerMock.h"
#include "ScrollingCoordinator.h"
#include "ScrollingMomentumCalculator.h"
#include "SecurityOrigin.h"
#include "SelectorFilter.h"
#include "SerializedScriptValue.h"
#include "ServiceWorker.h"
#include "ServiceWorkerProvider.h"
#include "ServiceWorkerRegistration.h"
#include "ServiceWorkerRegistrationData.h"
#include "Settings.h"
#include "ShadowRoot.h"
#include "ShouldPartitionCookie.h"
#include "SourceBuffer.h"
#include "SpeechSynthesisUtterance.h"
#include "SpellChecker.h"
#include "StaticNodeList.h"
#include "StorageNamespace.h"
#include "StorageNamespaceProvider.h"
#include "StreamTransferUtilities.h"
#include "StringCallback.h"
#include "StyleGridPosition.h"
#include "StyleResolver.h"
#include "StyleRule.h"
#include "StyleScope.h"
#include "StyleSheetContents.h"
#include "SystemSoundManager.h"
#include "TextIterator.h"
#include "TextPainter.h"
#include "TextPlaceholderElement.h"
#include "TextRecognitionOptions.h"
#include "ThreadableBlobRegistry.h"
#include "TreeScopeInlines.h"
#include "TypeConversions.h"
#include "UserContentURLPattern.h"
#include "UserGestureIndicator.h"
#include "UserMediaController.h"
#include "VideoConfiguration.h"
#include "ViewportArguments.h"
#include "VoidCallback.h"
#include "WebAnimation.h"
#include "WebAnimationUtilities.h"
#include "WebCodecsVideoDecoder.h"
#include "WebCoreJSClientData.h"
#include "WebRTCProvider.h"
#include "WindowProxy.h"
#include "WorkerThread.h"
#include "WorkletGlobalScope.h"
#include "WritingDirection.h"
#include "XMLHttpRequest.h"
#include <JavaScriptCore/CodeBlock.h>
#include <JavaScriptCore/FunctionExecutable.h>
#include <JavaScriptCore/InspectorAgentBase.h>
#include <JavaScriptCore/InspectorFrontendChannel.h>
#include <JavaScriptCore/JSCInlines.h>
#include <JavaScriptCore/JSCJSValue.h>
#include <wtf/FileHandle.h>
#include <wtf/FileSystem.h>
#include <wtf/HexNumber.h>
#include <wtf/JSONValues.h>
#include <wtf/Language.h>
#include <wtf/MemoryPressureHandler.h>
#include <wtf/MonotonicTime.h>
#include <wtf/NativePromise.h>
#include <wtf/ProcessID.h>
#include <wtf/RunLoop.h>
#include <wtf/TZoneMallocInlines.h>
#include <wtf/URLHelpers.h>
#include <wtf/WeakPtr.h>
#include <wtf/text/MakeString.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/StringToIntegerConversion.h>
#if USE(CG)
#include "PDFDocumentImage.h"
#endif
#if ENABLE(MOUSE_CURSOR_SCALE)
#include <wtf/dtoa.h>
#endif
#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
#include "LegacyCDM.h"
#include "LegacyMockCDM.h"
#endif
#if ENABLE(ENCRYPTED_MEDIA)
#include "MockCDMFactory.h"
#endif
#if ENABLE(VIDEO)
#include "CaptionUserPreferences.h"
#include "HTMLMediaElement.h"
#include "MockCaptionDisplaySettingsClientCallback.h"
#include "PageGroup.h"
#include "TextTrack.h"
#include "TextTrackCueGeneric.h"
#include "TimeRanges.h"
#include "VTTCue.h"
#endif
#if ENABLE(WEBGL)
#include "WebGLRenderingContext.h"
#endif
#if ENABLE(SPEECH_SYNTHESIS)
#include "LocalDOMWindowSpeechSynthesis.h"
#include "PlatformSpeechSynthesizerMock.h"
#include "SpeechSynthesis.h"
#endif
#if ENABLE(MEDIA_STREAM)
#include "MediaStream.h"
#include "MockRealtimeMediaSourceCenter.h"
#include "VideoFrame.h"
#endif
#if ENABLE(MEDIA_RECORDER)
#include "MediaRecorder.h"
#include "MediaRecorderPrivateMock.h"
#endif
#if ENABLE(WEB_RTC)
#include "RTCPeerConnection.h"
#endif
#if USE(LIBWEBRTC)
#include "LibWebRTCProvider.h"
#endif
#if ENABLE(CONTENT_FILTERING)
#include "MockContentFilterSettings.h"
#endif
#if ENABLE(WEB_AUDIO)
#include "AudioContext.h"
#include "WaveShaperDSPKernel.h"
#endif
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
#include "MediaPlaybackTargetMock.h"
#endif
#if ENABLE(POINTER_LOCK)
#include "PointerLockController.h"
#endif
#if USE(QUICK_LOOK)
#include "LegacyPreviewLoader.h"
#include "MockPreviewLoaderClient.h"
#endif
#if ENABLE(APPLE_PAY)
#include "MockPaymentCoordinator.h"
#include "PaymentCoordinator.h"
#endif
#if ENABLE(WEB_CODECS)
#include "JSWebCodecsVideoFrame.h"
#endif
#if ENABLE(WEBXR)
#include "NavigatorWebXR.h"
#include "WebXRSystem.h"
#include "WebXRTest.h"
#endif
#if PLATFORM(MAC)
#include "GraphicsChecksMac.h"
#include "ScrollbarsControllerMac.h"
#endif
#if PLATFORM(IOS_FAMILY)
#include "MediaSessionHelperIOS.h"
#endif
#if PLATFORM(COCOA)
#include "FontCacheCoreText.h"
#include "SystemBattery.h"
#include "VP9UtilitiesCocoa.h"
#include <pal/spi/cf/CoreTextSPI.h>
#include <wtf/spi/darwin/SandboxSPI.h>
#endif
#if PLATFORM(WPE)
#include <wtf/glib/GResources.h>
#endif
#if ENABLE(MEDIA_SESSION_COORDINATOR)
#include "MediaSessionCoordinator.h"
#include "MockMediaSessionCoordinator.h"
#include "NavigatorMediaSession.h"
#endif
#if ENABLE(MEDIA_SESSION) && USE(GLIB)
#include "MediaSessionManagerGLib.h"
#endif
#if ENABLE(IMAGE_ANALYSIS)
#include "TextRecognitionResult.h"
#endif
#if ENABLE(ARKIT_INLINE_PREVIEW_MAC) || ENABLE(MODEL_ELEMENT)
#include "HTMLModelElement.h"
#endif
#if ENABLE(SERVICE_CONTROLS)
#include "ImageControlsMac.h"
#endif
#if ENABLE(WIRELESS_PLAYBACK_MEDIA_PLAYER)
#import "MediaDeviceRouteController.h"
#import "MockMediaDeviceRouteController.h"
#endif
using JSC::CallData;
using JSC::CodeBlock;
using JSC::FunctionExecutable;
using JSC::Identifier;
using JSC::JSFunction;
using JSC::JSGlobalObject;
using JSC::JSObject;
using JSC::JSValue;
using JSC::MarkedArgumentBuffer;
using JSC::PropertySlot;
using JSC::ScriptExecutable;
using JSC::StackVisitor;
namespace WebCore {
WTF_MAKE_TZONE_ALLOCATED_IMPL(Internals);
using namespace Inspector;
using namespace HTMLNames;
class InspectorStubFrontend final : public InspectorFrontendClientLocal, public FrontendChannel {
WTF_MAKE_TZONE_ALLOCATED_INLINE(InspectorStubFrontend);
public:
InspectorStubFrontend(Page& inspectedPage, LocalFrame& mainFrame, RefPtr<LocalDOMWindow>&& frontendWindow);
virtual ~InspectorStubFrontend();
private:
bool supportsDockSide(DockSide) final { return false; }
void attachWindow(DockSide) final { }
void detachWindow() final { }
void closeWindow() final;
void reopen() final { }
void bringToFront() final { }
void setForcedAppearance(InspectorFrontendClient::Appearance) final { }
String localizedStringsURL() const final { return String(); }
DebuggableType debuggableType() const final { return DebuggableType::WebPage; }
String targetPlatformName() const { return "Unknown"_s; }
String targetBuildVersion() const { return "Unknown"_s; }
String targetProductVersion() const { return "Unknown"_s; }
bool targetIsSimulator() const { return false; }
void inspectedURLChanged(const String&) final { }
void showCertificate(const CertificateInfo&) final { }
void setAttachedWindowHeight(unsigned) final { }
void setAttachedWindowWidth(unsigned) final { }
void setSheetRect(const FloatRect&) final { }
void sendMessageToFrontend(const String& message) final;
ConnectionType connectionType() const final { return ConnectionType::Local; }
RefPtr<LocalDOMWindow> m_frontendWindow;
WeakPtr<FrameInspectorController> m_mainFrameInspectorController;
};
InspectorStubFrontend::InspectorStubFrontend(Page& inspectedPage, LocalFrame& mainFrame, RefPtr<LocalDOMWindow>&& frontendWindow)
: InspectorFrontendClientLocal(&inspectedPage.inspectorController(), frontendWindow->document()->page(), makeUnique<InspectorFrontendClientLocal::Settings>(), InspectorFrontendClientLocal::DispatchBackendTarget::MainFrame)
, m_frontendWindow(frontendWindow.copyRef())
, m_mainFrameInspectorController(mainFrame.inspectorController())
{
ASSERT_ARG(frontendWindow, frontendWindow);
frontendPage()->inspectorController().setInspectorFrontendClient(this);
inspectedPage.protectedInspectorController()->connectFrontend(*this);
mainFrame.protectedInspectorController()->connectFrontend(*this);
}
InspectorStubFrontend::~InspectorStubFrontend()
{
closeWindow();
}
void InspectorStubFrontend::closeWindow()
{
if (!m_frontendWindow)
return;
frontendPage()->inspectorController().setInspectorFrontendClient(nullptr);
if (RefPtr controller = m_mainFrameInspectorController.get())
controller->disconnectFrontend(*this);
if (RefPtr page = inspectedPage())
page->protectedInspectorController()->disconnectFrontend(*this);
m_frontendWindow->close();
m_frontendWindow = nullptr;
}
void InspectorStubFrontend::sendMessageToFrontend(const String& message)
{
frontendAPIDispatcher().dispatchMessageAsync(message);
}
static bool markerTypeFrom(const String& markerType, DocumentMarkerType& result)
{
if (equalLettersIgnoringASCIICase(markerType, "spelling"_s))
result = DocumentMarkerType::Spelling;
else if (equalLettersIgnoringASCIICase(markerType, "grammar"_s))
result = DocumentMarkerType::Grammar;
else if (equalLettersIgnoringASCIICase(markerType, "textmatch"_s))
result = DocumentMarkerType::TextMatch;
else if (equalLettersIgnoringASCIICase(markerType, "replacement"_s))
result = DocumentMarkerType::Replacement;
else if (equalLettersIgnoringASCIICase(markerType, "correctionindicator"_s))
result = DocumentMarkerType::CorrectionIndicator;
else if (equalLettersIgnoringASCIICase(markerType, "rejectedcorrection"_s))
result = DocumentMarkerType::RejectedCorrection;
else if (equalLettersIgnoringASCIICase(markerType, "autocorrected"_s))
result = DocumentMarkerType::Autocorrected;
else if (equalLettersIgnoringASCIICase(markerType, "spellcheckingexemption"_s))
result = DocumentMarkerType::SpellCheckingExemption;
else if (equalLettersIgnoringASCIICase(markerType, "deletedautocorrection"_s))
result = DocumentMarkerType::DeletedAutocorrection;
else if (equalLettersIgnoringASCIICase(markerType, "dictationalternatives"_s))
result = DocumentMarkerType::DictationAlternatives;
#if ENABLE(TELEPHONE_NUMBER_DETECTION)
else if (equalLettersIgnoringASCIICase(markerType, "telephonenumber"_s))
result = DocumentMarkerType::TelephoneNumber;
#endif
#if ENABLE(WRITING_TOOLS)
else if (equalLettersIgnoringASCIICase(markerType, "writingtoolstextsuggestion"_s))
result = DocumentMarkerType::WritingToolsTextSuggestion;
#endif
else if (equalLettersIgnoringASCIICase(markerType, "transparentcontent"_s))
result = DocumentMarkerType::TransparentContent;
else
return false;
return true;
}
static bool markerTypesFrom(const String& markerType, OptionSet<DocumentMarkerType>& result)
{
DocumentMarkerType singularResult;
if (markerType.isEmpty() || equalLettersIgnoringASCIICase(markerType, "all"_s))
result = DocumentMarker::allMarkers();
else if (markerTypeFrom(markerType, singularResult))
result = singularResult;
else
return false;
return true;
}
static RefPtr<PrintContext>& printContextForTesting()
{
static NeverDestroyed<RefPtr<PrintContext>> context;
return context;
}
Ref<Internals> Internals::create(Document& document)
{
return adoptRef(*new Internals(document));
}
Internals::~Internals()
{
#if ENABLE(MEDIA_STREAM)
stopObservingRealtimeMediaSource();
#endif
#if ENABLE(MEDIA_SESSION) && ENABLE(WEB_CODECS)
if (m_artworkImagePromise)
m_artworkImagePromise->reject(Exception { ExceptionCode::InvalidStateError });
#endif
}
void Internals::resetToConsistentState(Page& page)
{
page.setPageScaleFactor(1, IntPoint(0, 0));
page.setPagination(Pagination());
page.setDefersLoading(false);
page.setResourceCachingDisabledByWebInspector(false);
page.setConsoleMessageListenerForTesting(nullptr);
RefPtr localMainFrame = page.localMainFrame();
if (!localMainFrame)
return;
localMainFrame->setTextZoomFactor(1.0f);
page.setCompositingPolicyOverride(WebCore::CompositingPolicy::Normal);
auto* mainFrameView = localMainFrame->view();
if (mainFrameView) {
page.setHeaderHeight(0);
page.setFooterHeight(0);
page.setObscuredContentInsets({ });
mainFrameView->setUseFixedLayout(false);
mainFrameView->setFixedLayoutSize(IntSize());
mainFrameView->enableFixedWidthAutoSizeMode(false, { });
if (auto* backing = mainFrameView->tiledBacking())
backing->setTileSizeUpdateDelayDisabledForTesting(false);
}
if (RefPtr window = localMainFrame->window())
window->history().setTotalStateObjectPayloadLimitOverride(std::nullopt);
WTF::clearDefaultPortForProtocolMapForTesting();
overrideUserPreferredLanguages(Vector<String>());
WebCore::DeprecatedGlobalSettings::setUsesOverlayScrollbars(false);
if (!localMainFrame->editor().isContinuousSpellCheckingEnabled())
localMainFrame->editor().toggleContinuousSpellChecking();
if (localMainFrame->editor().isOverwriteModeEnabled())
localMainFrame->editor().toggleOverwriteModeEnabled();
localMainFrame->loader().clearTestingOverrides();
RefPtr sessionManager = page.mediaSessionManager();
#if ENABLE(VIDEO)
page.group().ensureCaptionPreferences().setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode::ForcedOnly);
page.group().ensureCaptionPreferences().setCaptionsStyleSheetOverride(emptyString());
page.group().ensureCaptionPreferences().setPreferredLanguage(emptyString());
sessionManager->resetHaveEverRegisteredAsNowPlayingApplicationForTesting();
sessionManager->resetRestrictions();
sessionManager->resetSessionState();
sessionManager->setWillIgnoreSystemInterruptions(true);
sessionManager->applicationWillEnterForeground(false);
if (page.mediaPlaybackIsSuspended())
page.resumeAllMediaPlayback();
#endif
#if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
sessionManager->setIsPlayingToAutomotiveHeadUnit(false);
#endif
AXObjectCache::setEnhancedUserInterfaceAccessibility(false);
AXObjectCache::disableAccessibility();
MockPageOverlayClient::singleton().uninstallAllOverlays();
#if ENABLE(CONTENT_FILTERING)
MockContentFilterSettings::reset();
#endif
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
page.setMockMediaPlaybackTargetPickerEnabled(true);
page.setMockMediaPlaybackTargetPickerState(emptyString(), MediaPlaybackTargetMock::State::Unknown);
#endif
#if ENABLE(VIDEO)
MediaResourceLoader::recordResponsesForTesting();
#endif
page.setShowAllPlugins(false);
page.setLowPowerModeEnabledOverrideForTesting(std::nullopt);
page.setAggressiveThermalMitigationEnabledForTesting(std::nullopt);
page.setOutsideViewportThrottlingEnabledForTesting(false);
#if USE(QUICK_LOOK)
MockPreviewLoaderClient::singleton().setPassword(emptyString());
LegacyPreviewLoader::setClientForTesting(nullptr);
#endif
printContextForTesting() = nullptr;
#if ENABLE(WEB_RTC)
auto& rtcProvider = page.webRTCProvider();
#if USE(LIBWEBRTC)
auto& webRTCProvider = downcast<LibWebRTCProvider>(rtcProvider);
WebCore::useRealRTCPeerConnectionFactory(webRTCProvider);
webRTCProvider.disableNonLocalhostConnections();
webRTCProvider.setVP9HardwareSupportForTesting({ });
#endif
page.settings().setWebRTCEncryptionEnabled(true);
rtcProvider.setH265Support(true);
rtcProvider.setVP9Support(true, true);
rtcProvider.clearFactory();
#if USE(GSTREAMER_WEBRTC)
page.settings().setPeerConnectionEnabled(true);
#endif
#endif
page.setFullscreenAutoHideDuration(0_s);
page.setFullscreenInsets({ });
PlatformMediaEngineConfigurationFactory::disableMock();
#if ENABLE(MEDIA_STREAM)
page.settings().setInterruptAudioOnPageVisibilityChangeEnabled(false);
#endif
#if ENABLE(MEDIA_RECORDER)
WebCore::MediaRecorder::setCustomPrivateRecorderCreator(nullptr);
#endif
CanvasBase::setMaxCanvasAreaForTesting(std::nullopt);
LocalDOMWindow::overrideTransientActivationDurationForTesting(std::nullopt);
#if PLATFORM(IOS) || PLATFORM(VISION)
WebCore::setContentSizeCategory(kCTFontContentSizeCategoryL);
#endif
#if ENABLE(MEDIA_SESSION) && USE(GLIB)
MediaSessionManagerGLib* glibSessionManager = static_cast<MediaSessionManagerGLib*>(sessionManager.get());
glibSessionManager->setDBusNotificationsEnabled(false);
#endif
#if PLATFORM(COCOA)
setOverrideEnhanceTextLegibility(false);
#endif
TextPainter::setForceUseGlyphDisplayListForTesting(false);
#if USE(AUDIO_SESSION)
AudioSession::singleton().setCategoryOverride(AudioSessionCategory::None);
AudioSession::singleton().tryToSetActive(false);
AudioSession::singleton().endInterruptionForTesting();
#endif
#if ENABLE(DAMAGE_TRACKING)
page.chrome().client().resetDamageHistoryForTesting();
#endif
#if ENABLE(WIRELESS_PLAYBACK_MEDIA_PLAYER)
MediaDeviceRouteController::singleton().setClient(nullptr);
setMockMediaDeviceRouteControllerEnabled(false);
#endif
}
Internals::Internals(Document& document)
: ContextDestructionObserver(&document)
#if ENABLE(MEDIA_STREAM)
, m_orientationNotifier(0)
#endif
{
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
if (document.page())
document.page()->setMockMediaPlaybackTargetPickerEnabled(true);
#endif
#if ENABLE(VIDEO)
if (document.page())
m_testingModeToken = document.page()->group().ensureCaptionPreferences().createTestingModeToken().moveToUniquePtr();
#endif
if (contextDocument() && contextDocument()->frame()) {
setAutomaticSpellingCorrectionEnabled(true);
setAutomaticQuoteSubstitutionEnabled(false);
setAutomaticDashSubstitutionEnabled(false);
setAutomaticLinkDetectionEnabled(false);
setAutomaticTextReplacementEnabled(true);
}
#if ENABLE(APPLE_PAY)
RefPtr frame = document.frame();
if (frame && frame->page() && frame->isMainFrame()) {
auto mockPaymentCoordinator = MockPaymentCoordinator::create(*frame->page());
frame->page()->setPaymentCoordinator(PaymentCoordinator::create(WTF::move(mockPaymentCoordinator)));
}
#endif
#if PLATFORM(COCOA) && ENABLE(WEB_AUDIO)
AudioDestinationCocoa::createOverride = nullptr;
#endif
#if PLATFORM(COCOA)
SystemBatteryStatusTestingOverrides::singleton().resetOverridesToDefaultValues();
#endif
#if ENABLE(VP9) && PLATFORM(COCOA)
VP9TestingOverrides::singleton().resetOverridesToDefaultValues();
#endif
Scrollbar::setShouldUseFixedPixelsPerLineStepForTesting(true);
#if ENABLE(DAMAGE_TRACKING)
document.page()->chrome().client().resetDamageHistoryForTesting();
#endif
#if ENABLE(WIRELESS_PLAYBACK_MEDIA_PLAYER)
if (RefPtr mockMediaDeviceRouteController = m_mockMediaDeviceRouteController) {
m_mockMediaDeviceRouteController->setEnabled(false);
m_mockMediaDeviceRouteController = nullptr;
}
#endif
}
Document* Internals::contextDocument() const
{
return downcast<Document>(scriptExecutionContext());
}
LocalFrame* Internals::frame() const
{
if (!contextDocument())
return nullptr;
return contextDocument()->frame();
}
InternalSettings* Internals::settings() const
{
RefPtr document = contextDocument();
if (!document)
return nullptr;
RefPtr page = document->page();
if (!page)
return nullptr;
return InternalSettings::from(page);
}
unsigned Internals::inflightBeaconsCount() const
{
RefPtr document = contextDocument();
if (!document)
return 0;
auto* window = document->window();
if (!window)
return 0;
auto* navigator = window->optionalNavigator();
if (!navigator)
return 0;
return NavigatorBeacon::from(*navigator)->inflightBeaconsCount();
}
unsigned Internals::workerThreadCount() const
{
return WorkerThread::workerThreadCount();
}
ExceptionOr<bool> Internals::areSVGAnimationsPaused() const
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError, "No context document"_s };
if (!document->svgExtensionsIfExists())
return Exception { ExceptionCode::NotFoundError, "No SVG animations"_s };
return document->checkedSVGExtensions()->areAnimationsPaused();
}
ExceptionOr<double> Internals::svgAnimationsInterval(SVGSVGElement& element) const
{
RefPtr document = contextDocument();
if (!document)
return 0;
CheckedPtr svgExtensions = document->svgExtensionsIfExists();
if (!svgExtensions)
return 0;
if (svgExtensions->areAnimationsPaused())
return 0;
return element.timeContainer().animationFrameDelay().value();
}
Vector<Ref<SVGSVGElement>> Internals::allSVGSVGElements() const
{
Vector<Ref<SVGSVGElement>> elements;
for (auto& checkedDocument : Document::allDocuments()) {
Ref document = checkedDocument.get();
if (CheckedPtr svgExtensions = document->svgExtensionsIfExists())
elements.appendVector(svgExtensions->allSVGSVGElements());
}
return elements;
}
String Internals::address(Node& node)
{
return makeString("0x"_s, hex(reinterpret_cast<uintptr_t>(&node)));
}
bool Internals::nodeNeedsStyleRecalc(Node& node)
{
return node.needsStyleRecalc();
}
static String styleValidityToToString(Style::Validity validity)
{
switch (validity) {
case Style::Validity::Valid:
return "NoStyleChange"_s;
case Style::Validity::AnimationInvalid:
return "AnimationInvalid"_s;
case Style::Validity::InlineStyleInvalid:
return "InlineStyleInvalid"_s;
case Style::Validity::ElementInvalid:
return "InlineStyleChange"_s;
case Style::Validity::SubtreeInvalid:
return "FullStyleChange"_s;
}
ASSERT_NOT_REACHED();
return emptyString();
}
String Internals::styleChangeType(Node& node)
{
node.document().styleScope().flushPendingUpdate();
return styleValidityToToString(node.styleValidity());
}
String Internals::description(JSC::JSValue value)
{
return ::toString(value);
}
void Internals::log(const String& value)
{
WTFLogAlways("%s", value.utf8().data());
}
bool Internals::isPreloaded(const String& url)
{
RefPtr document = contextDocument();
return document->cachedResourceLoader().isPreloaded(url);
}
bool Internals::isLoadingFromMemoryCache(const String& url)
{
CachedResource* resource = resourceFromMemoryCache(url);
return resource && resource->status() == CachedResource::Cached;
}
CachedResource* Internals::resourceFromMemoryCache(const String& url)
{
if (!contextDocument() || !contextDocument()->page())
return nullptr;
ResourceRequest request(contextDocument()->completeURL(url));
request.setDomainForCachePartition(contextDocument()->domainForCachePartition());
return MemoryCache::singleton().resourceForRequest(request, contextDocument()->page()->sessionID());
}
static String responseSourceToString(const ResourceResponse& response)
{
if (response.isNull())
return "Null response"_s;
switch (response.source()) {
case ResourceResponse::Source::Unknown:
return "Unknown"_s;
case ResourceResponse::Source::Network:
return "Network"_s;
case ResourceResponse::Source::ServiceWorker:
return "Service worker"_s;
case ResourceResponse::Source::DiskCache:
return "Disk cache"_s;
case ResourceResponse::Source::DiskCacheAfterValidation:
return "Disk cache after validation"_s;
case ResourceResponse::Source::MemoryCache:
return "Memory cache"_s;
case ResourceResponse::Source::MemoryCacheAfterValidation:
return "Memory cache after validation"_s;
case ResourceResponse::Source::LegacyApplicationCachePlaceholder:
return "Application cache"_s;
case ResourceResponse::Source::DOMCache:
return "DOM cache"_s;
case ResourceResponse::Source::InspectorOverride:
return "Inspector override"_s;
}
ASSERT_NOT_REACHED();
return "Error"_s;
}
String Internals::xhrResponseSource(XMLHttpRequest& request)
{
return responseSourceToString(request.resourceResponse());
}
String Internals::fetchResponseSource(FetchResponse& response)
{
return responseSourceToString(response.resourceResponse());
}
String Internals::blobInternalURL(const Blob& blob)
{
return blob.url().string();
}
void Internals::isBlobInternalURLRegistered(const String& url, DOMPromiseDeferred<IDLBoolean>&& promise)
{
promise.resolve(!!ThreadableBlobRegistry::blobSize(URL { url }));
}
bool Internals::isSharingStyleSheetContents(HTMLLinkElement& a, HTMLLinkElement& b)
{
if (!a.sheet() || !b.sheet())
return false;
return &a.sheet()->contents() == &b.sheet()->contents();
}
bool Internals::isStyleSheetLoadingSubresources(HTMLLinkElement& link)
{
return link.sheet() && link.sheet()->contents().isLoadingSubresources();
}
static ResourceRequestCachePolicy toResourceRequestCachePolicy(Internals::CachePolicy policy)
{
switch (policy) {
case Internals::CachePolicy::UseProtocolCachePolicy:
return ResourceRequestCachePolicy::UseProtocolCachePolicy;
case Internals::CachePolicy::ReloadIgnoringCacheData:
return ResourceRequestCachePolicy::ReloadIgnoringCacheData;
case Internals::CachePolicy::ReturnCacheDataElseLoad:
return ResourceRequestCachePolicy::ReturnCacheDataElseLoad;
case Internals::CachePolicy::ReturnCacheDataDontLoad:
return ResourceRequestCachePolicy::ReturnCacheDataDontLoad;
}
ASSERT_NOT_REACHED();
return ResourceRequestCachePolicy::UseProtocolCachePolicy;
}
void Internals::setOverrideCachePolicy(CachePolicy policy)
{
frame()->loader().setOverrideCachePolicyForTesting(toResourceRequestCachePolicy(policy));
}
ExceptionOr<void> Internals::setCanShowModalDialogOverride(bool allow)
{
if (!contextDocument() || !contextDocument()->window())
return Exception { ExceptionCode::InvalidAccessError };
contextDocument()->window()->setCanShowModalDialogOverride(allow);
return { };
}
static ResourceLoadPriority toResourceLoadPriority(Internals::ResourceLoadPriority priority)
{
switch (priority) {
case Internals::ResourceLoadPriority::ResourceLoadPriorityVeryLow:
return ResourceLoadPriority::VeryLow;
case Internals::ResourceLoadPriority::ResourceLoadPriorityLow:
return ResourceLoadPriority::Low;
case Internals::ResourceLoadPriority::ResourceLoadPriorityMedium:
return ResourceLoadPriority::Medium;
case Internals::ResourceLoadPriority::ResourceLoadPriorityHigh:
return ResourceLoadPriority::High;
case Internals::ResourceLoadPriority::ResourceLoadPriorityVeryHigh:
return ResourceLoadPriority::VeryHigh;
}
ASSERT_NOT_REACHED();
return ResourceLoadPriority::Low;
}
void Internals::setOverrideResourceLoadPriority(ResourceLoadPriority priority)
{
frame()->loader().setOverrideResourceLoadPriorityForTesting(toResourceLoadPriority(priority));
}
void Internals::setStrictRawResourceValidationPolicyDisabled(bool disabled)
{
if (RefPtr localFrame = frame())
localFrame->loader().setStrictRawResourceValidationPolicyDisabledForTesting(disabled);
}
static Internals::ResourceLoadPriority toInternalsResourceLoadPriority(ResourceLoadPriority priority)
{
switch (priority) {
case ResourceLoadPriority::VeryLow:
return Internals::ResourceLoadPriority::ResourceLoadPriorityVeryLow;
case ResourceLoadPriority::Low:
return Internals::ResourceLoadPriority::ResourceLoadPriorityLow;
case ResourceLoadPriority::Medium:
return Internals::ResourceLoadPriority::ResourceLoadPriorityMedium;
case ResourceLoadPriority::High:
return Internals::ResourceLoadPriority::ResourceLoadPriorityHigh;
case ResourceLoadPriority::VeryHigh:
return Internals::ResourceLoadPriority::ResourceLoadPriorityVeryHigh;
}
ASSERT_NOT_REACHED();
return Internals::ResourceLoadPriority::ResourceLoadPriorityLow;
}
std::optional<Internals::ResourceLoadPriority> Internals::getResourcePriority(const String& url)
{
RefPtr document = contextDocument();
if (!document)
return std::nullopt;
auto* resource = document->cachedResourceLoader().cachedResource(url);
if (!resource)
resource = resourceFromMemoryCache(url);
if (resource)
return toInternalsResourceLoadPriority(resource->loadPriority());
return std::nullopt;
}
bool Internals::isFetchObjectContextStopped(const FetchObject& object)
{
return switchOn(object, [](const RefPtr<FetchRequest>& request) {
return request->isContextStopped();
}, [](auto& response) {
return response->isContextStopped();
});
}
void Internals::clearMemoryCache()
{
MemoryCache::singleton().evictResources();
CrossOriginPreflightResultCache::singleton().clear();
}
void Internals::pruneMemoryCacheToSize(unsigned size)
{
MemoryCache::singleton().pruneDeadResourcesToSize(size);
MemoryCache::singleton().pruneLiveResourcesToSize(size, true);
}
void Internals::destroyDecodedDataForAllImages()
{
MemoryCache::singleton().destroyDecodedDataForAllImages();
}
unsigned Internals::memoryCacheSize() const
{
return MemoryCache::singleton().size();
}
static Image* imageFromImageElement(HTMLImageElement& element)
{
auto* cachedImage = element.cachedImage();
return cachedImage ? cachedImage->image() : nullptr;
}
static BitmapImage* bitmapImageFromImageElement(HTMLImageElement& element)
{
return dynamicDowncast<BitmapImage>(imageFromImageElement(element));
}
#if USE(CG)
static PDFDocumentImage* pdfDocumentImageFromImageElement(HTMLImageElement& element)
{
return dynamicDowncast<PDFDocumentImage>(imageFromImageElement(element));
}
#endif
unsigned Internals::imageFrameIndex(HTMLImageElement& element)
{
RefPtr bitmapImage = bitmapImageFromImageElement(element);
return bitmapImage ? bitmapImage->currentFrameIndex() : 0;
}
unsigned Internals::imageFrameCount(HTMLImageElement& element)
{
RefPtr bitmapImage = bitmapImageFromImageElement(element);
return bitmapImage ? bitmapImage->frameCount() : 0;
}
float Internals::imageFrameDurationAtIndex(HTMLImageElement& element, unsigned index)
{
RefPtr bitmapImage = bitmapImageFromImageElement(element);
return bitmapImage ? bitmapImage->frameDurationAtIndex(index).value() : 0;
}
void Internals::setImageFrameDecodingDuration(HTMLImageElement& element, float duration)
{
if (RefPtr bitmapImage = bitmapImageFromImageElement(element))
bitmapImage->setMinimumDecodingDurationForTesting(Seconds { duration });
}
void Internals::resetImageAnimation(HTMLImageElement& element)
{
if (RefPtr image = imageFromImageElement(element))
image->resetAnimation();
}
bool Internals::isImageAnimating(HTMLImageElement& element)
{
auto* image = imageFromImageElement(element);
return image && (image->isAnimating() || image->animationPending());
}
#if ENABLE(ACCESSIBILITY_ANIMATION_CONTROL)
void Internals::setImageAnimationEnabled(bool enabled)
{
if (RefPtr page = contextDocument() ? contextDocument()->page() : nullptr) {
// We need to set this here to mimic the behavior of the AX preference changing
Image::setSystemAllowsAnimationControls(!enabled);
page->setImageAnimationEnabled(enabled);
}
}
void Internals::resumeImageAnimation(HTMLImageElement& element)
{
element.setAllowsAnimation(true);
}
void Internals::pauseImageAnimation(HTMLImageElement& element)
{
element.setAllowsAnimation(false);
}
#endif // ENABLE(ACCESSIBILITY_ANIMATION_CONTROL)
#if ENABLE(ACCESSIBILITY_NON_BLINKING_CURSOR)
void Internals::setPrefersNonBlinkingCursor(bool enabled)
{
RefPtr document = contextDocument();
if (RefPtr page = document ? document->page() : nullptr) {
page->setPrefersNonBlinkingCursor(enabled);
page->forEachDocument([&](auto& document) {
document.selection().setPrefersNonBlinkingCursor(enabled);
});
}
}
#endif
unsigned Internals::imagePendingDecodePromisesCountForTesting(HTMLImageElement& element)
{
return element.pendingDecodePromisesCountForTesting();
}
void Internals::setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement& element, bool enabled)
{
if (RefPtr bitmapImage = bitmapImageFromImageElement(element))
bitmapImage->setClearDecoderAfterAsyncFrameRequestForTesting(enabled);
}
unsigned Internals::imageDecodeCount(HTMLImageElement& element)
{
RefPtr bitmapImage = bitmapImageFromImageElement(element);
return bitmapImage ? bitmapImage->decodeCountForTesting() : 0;
}
unsigned Internals::imageBlankDrawCount(HTMLImageElement& element)
{
RefPtr bitmapImage = bitmapImageFromImageElement(element);
return bitmapImage ? bitmapImage->blankDrawCountForTesting() : 0;
}
AtomString Internals::imageLastDecodingOptions(HTMLImageElement& element)
{
RefPtr bitmapImage = bitmapImageFromImageElement(element);
if (!bitmapImage)
return { };
auto options = bitmapImage->currentFrameDecodingOptions();
StringBuilder builder;
builder.append("{ decodingMode : "_s,
options.decodingMode() == DecodingMode::Asynchronous ? "Asynchronous"_s : "Synchronous"_s);
if (auto sizeForDrawing = options.sizeForDrawing()) {
builder.append(", sizeForDrawing : { "_s, sizeForDrawing->width(),
", "_s, sizeForDrawing->height(), " }"_s);
}
builder.append(" }"_s);
return builder.toAtomString();
}
unsigned Internals::imageCachedSubimageCreateCount(HTMLImageElement& element)
{
#if USE(CG)
if (auto* pdfDocumentImage = pdfDocumentImageFromImageElement(element))
return pdfDocumentImage->cachedSubimageCreateCountForTesting();
#else
UNUSED_PARAM(element);
#endif
return 0;
}
unsigned Internals::remoteImagesCountForTesting() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return 0;
return document->page()->chrome().client().remoteImagesCountForTesting();
}
void Internals::setAsyncDecodingEnabledForTesting(HTMLImageElement& element, bool enabled)
{
if (RefPtr bitmapImage = bitmapImageFromImageElement(element))
bitmapImage->setAsyncDecodingEnabledForTesting(enabled);
}
void Internals::setForceUpdateImageDataEnabledForTesting(HTMLImageElement& element, bool enabled)
{
if (auto* cachedImage = element.cachedImage())
cachedImage->setForceUpdateImageDataEnabledForTesting(enabled);
}
void Internals::setHasHDRContentForTesting(HTMLImageElement& element)
{
if (RefPtr bitmapImage = bitmapImageFromImageElement(element))
bitmapImage->setHasHDRContentForTesting();
}
#if ENABLE(WEB_CODECS)
bool Internals::hasPendingActivity(const WebCodecsVideoDecoder& decoder) const
{
return decoder.hasPendingActivity();
}
#endif
void Internals::setGridMaxTracksLimit(unsigned maxTrackLimit)
{
Style::GridPosition::setMaxPositionForTesting(maxTrackLimit);
}
void Internals::clearBackForwardCache()
{
BackForwardCache::singleton().pruneToSizeNow(0, PruningReason::None);
}
unsigned Internals::backForwardCacheSize() const
{
return BackForwardCache::singleton().pageCount();
}
void Internals::preventDocumentFromEnteringBackForwardCache()
{
if (RefPtr document = contextDocument())
document->preventEnteringBackForwardCacheForTesting();
}
void Internals::disableTileSizeUpdateDelay()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return;
auto* view = document->frame()->view();
if (!view)
return;
if (auto* backing = view->tiledBacking())
backing->setTileSizeUpdateDelayDisabledForTesting(true);
}
void Internals::setSpeculativeTilingDelayDisabledForTesting(bool disabled)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return;
if (auto* frameView = document->frame()->view())
frameView->setSpeculativeTilingDelayDisabledForTesting(disabled);
}
Node& Internals::treeScopeRootNode(Node& node)
{
return node.treeScope().rootNode();
}
Node* Internals::parentTreeScope(Node& node)
{
const TreeScope* parentTreeScope = node.treeScope().parentTreeScope();
return parentTreeScope ? &parentTreeScope->rootNode() : nullptr;
}
ExceptionOr<unsigned> Internals::lastSpatialNavigationCandidateCount() const
{
if (!contextDocument() || !contextDocument()->page())
return Exception { ExceptionCode::InvalidAccessError };
return contextDocument()->page()->lastSpatialNavigationCandidateCount();
}
bool Internals::animationWithIdExists(const String& id) const
{
for (auto& animation : WebAnimation::instances()) {
if (animation->id() == id)
return true;
}
return false;
}
unsigned Internals::numberOfActiveAnimations() const
{
RefPtr localFrame = frame();
return localFrame ? localFrame->document()->timeline().numberOfActiveAnimationsForTesting() : 0;
}
ExceptionOr<bool> Internals::animationsAreSuspended() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
return document->ensureTimelinesController().animationsAreSuspended();
}
double Internals::animationsInterval() const
{
RefPtr document = contextDocument();
if (!document)
return INFINITY;
if (auto timeline = document->existingTimeline())
return timeline->animationInterval().seconds();
return INFINITY;
}
ExceptionOr<void> Internals::suspendAnimations() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->ensureTimelinesController().suspendAnimations();
for (RefPtr<Frame> frame = document->frame(); frame; frame = frame->tree().traverseNext()) {
RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
if (!localFrame)
continue;
if (RefPtr document = localFrame->document())
document->ensureTimelinesController().suspendAnimations();
}
return { };
}
ExceptionOr<void> Internals::resumeAnimations() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->ensureTimelinesController().resumeAnimations();
for (RefPtr<Frame> frame = document->frame(); frame; frame = frame->tree().traverseNext()) {
RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
if (!localFrame)
continue;
if (RefPtr document = localFrame->document())
document->ensureTimelinesController().resumeAnimations();
}
return { };
}
uint64_t Internals::identifierForTimeline(AnimationTimeline& timeline) const
{
#if ENABLE(THREADED_ANIMATIONS)
return timeline.acceleratedTimelineIdentifier().toRawValue();
#else
UNUSED_PARAM(timeline);
return 0;
#endif
}
Internals::ScrollingNodeID Internals::scrollingNodeIDForTimeline(AnimationTimeline& timeline) const
{
#if ENABLE(THREADED_ANIMATIONS)
if (RefPtr scrollTimeline = dynamicDowncast<ScrollTimeline>(timeline)) {
if (auto scrollingNodeID = scrollTimeline->scrollingNodeIDForTesting())
return { scrollingNodeID->object().toUInt64(), scrollingNodeID->processIdentifier().toUInt64() };
}
#else
UNUSED_PARAM(timeline);
#endif
return { 0, 0 };
}
Vector<Internals::AcceleratedAnimation> Internals::acceleratedAnimationsForElement(Element& element)
{
CheckedPtr timelinesController = element.document().timelinesController();
if (!timelinesController)
return { };
Vector<Internals::AcceleratedAnimation> animations;
for (const auto& acceleratedAnimation : timelinesController->acceleratedAnimationsForElement(element))
animations.append({ acceleratedAnimation.property, acceleratedAnimation.speed, acceleratedAnimation.isThreaded });
return animations;
}
unsigned Internals::numberOfAnimationTimelineInvalidations() const
{
RefPtr localFrame = frame();
return localFrame ? localFrame->document()->timeline().numberOfAnimationTimelineInvalidationsForTesting() : 0;
}
double Internals::timeToNextAnimationTick(WebAnimation& animation) const
{
return secondsToWebAnimationsAPITime(animation.timeToNextTick());
}
ExceptionOr<RefPtr<Element>> Internals::pseudoElement(Element& element, const String& pseudoId)
{
if (pseudoId != "before"_s && pseudoId != "after"_s)
return Exception { ExceptionCode::InvalidAccessError };
return pseudoId == "before"_s ? element.beforePseudoElement() : element.afterPseudoElement();
}
double Internals::preferredRenderingUpdateInterval()
{
RefPtr document = contextDocument();
if (!document)
return 0;
RefPtr page = document->page();
if (!page)
return 0;
return page->preferredRenderingUpdateInterval().milliseconds();
}
ExceptionOr<String> Internals::elementRenderTreeAsText(Element& element)
{
element.document().updateStyleIfNeeded();
String representation = externalRepresentation(&element);
if (representation.isEmpty())
return Exception { ExceptionCode::InvalidAccessError };
return representation;
}
bool Internals::hasPausedImageAnimations(Element& element)
{
return element.renderer() && element.renderer()->hasPausedImageAnimations();
}
bool Internals::isFullyActive(Document& document)
{
return document.isFullyActive();
}
bool Internals::isPaintingFrequently(Element& element)
{
return element.renderer() && element.renderer()->enclosingLayer() && element.renderer()->enclosingLayer()->paintingFrequently();
}
void Internals::incrementFrequentPaintCounter(Element& element)
{
if (element.renderer() && element.renderer()->enclosingLayer())
element.renderer()->enclosingLayer()->simulateFrequentPaint();
}
void Internals::purgeFrontBuffer(Element& element)
{
if (element.renderer()) {
if (CheckedPtr layer = element.renderer()->enclosingLayer())
layer->purgeFrontBufferForTesting();
}
}
void Internals::purgeBackBuffer(Element& element)
{
if (element.renderer()) {
if (CheckedPtr layer = element.renderer()->enclosingLayer())
layer->purgeBackBufferForTesting();
}
}
void Internals::markFrontBufferVolatile(Element& element)
{
if (element.renderer()) {
if (CheckedPtr layer = element.renderer()->enclosingLayer())
layer->markFrontBufferVolatileForTesting();
}
}
Ref<CSSComputedStyleDeclaration> Internals::computedStyleIncludingVisitedInfo(Element& element) const
{
return CSSComputedStyleDeclaration::create(element, CSSComputedStyleDeclaration::AllowVisited::Yes);
}
Node& Internals::ensureUserAgentShadowRoot(Element& host)
{
return host.ensureUserAgentShadowRoot();
}
Node* Internals::shadowRoot(Element& host)
{
if (host.document().hasElementWithPendingUserAgentShadowTreeUpdate(host)) {
host.updateUserAgentShadowTree();
host.document().removeElementWithPendingUserAgentShadowTreeUpdate(host);
}
return host.shadowRoot();
}
ExceptionOr<String> Internals::shadowRootType(const Node& root) const
{
auto* shadowRoot = dynamicDowncast<ShadowRoot>(root);
if (!shadowRoot)
return Exception { ExceptionCode::InvalidAccessError };
switch (shadowRoot->mode()) {
case ShadowRootMode::UserAgent:
return "UserAgentShadowRoot"_str;
case ShadowRootMode::Closed:
return "ClosedShadowRoot"_str;
case ShadowRootMode::Open:
return "OpenShadowRoot"_str;
default:
ASSERT_NOT_REACHED();
return "Unknown"_str;
}
}
const AtomString& Internals::userAgentPart(Element& element)
{
return element.userAgentPart();
}
void Internals::setUserAgentPart(Element& element, const AtomString& part)
{
return element.setUserAgentPart(part);
}
ExceptionOr<bool> Internals::isTimerThrottled(int timeoutId)
{
auto* timer = scriptExecutionContext()->findTimeout(timeoutId);
if (!timer)
return Exception { ExceptionCode::NotFoundError };
if (timer->intervalClampedToMinimum() > timer->m_originalInterval)
return true;
constexpr MonotonicTime unalignedFireTime { };
constexpr MonotonicTime alignmentForMaximallyNestedTimerToBeConsideredUnthrottled = unalignedFireTime + 2.0 * DOMTimer::minimumAlignmentForMaximallyNestedTimers();
constexpr MonotonicTime alignmentForLowNestingTimerToBeConsideredUnthrottled = unalignedFireTime + 2.0 * DOMTimer::defaultAlignmentInterval();
MonotonicTime alignedFireTime = scriptExecutionContext()->alignedFireTime(timer->hasReachedMaxNestingLevel(), unalignedFireTime);
if (timer->hasReachedMaxNestingLevel())
return alignedFireTime > alignmentForMaximallyNestedTimerToBeConsideredUnthrottled;
return alignedFireTime > alignmentForLowNestingTimerToBeConsideredUnthrottled;
}
ExceptionOr<bool> Internals::isTimerAligned(int timeoutId)
{
auto* timer = scriptExecutionContext()->findTimeout(timeoutId);
if (!timer)
return Exception { ExceptionCode::NotFoundError };
constexpr MonotonicTime unalignedFireTime { };
MonotonicTime alignedFireTime = scriptExecutionContext()->alignedFireTime(timer->hasReachedMaxNestingLevel(), unalignedFireTime);
return alignedFireTime != unalignedFireTime;
}
String Internals::requestAnimationFrameThrottlingReasons() const
{
auto* scriptedAnimationController = contextDocument()->scriptedAnimationController();
if (!scriptedAnimationController)
return String();
TextStream ts;
ts << scriptedAnimationController->throttlingReasons();
return ts.release();
}
double Internals::requestAnimationFrameInterval() const
{
auto* scriptedAnimationController = contextDocument()->scriptedAnimationController();
if (!scriptedAnimationController)
return INFINITY;
return scriptedAnimationController->interval().value();
}
bool Internals::scriptedAnimationsAreSuspended() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return true;
return document->page()->scriptedAnimationsSuspended();
}
bool Internals::areTimersThrottled() const
{
return contextDocument()->isTimerThrottlingEnabled();
}
void Internals::setEventThrottlingBehaviorOverride(std::optional<EventThrottlingBehavior> value)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return;
if (!value) {
document->page()->setEventThrottlingBehaviorOverride(std::nullopt);
return;
}
switch (value.value()) {
case Internals::EventThrottlingBehavior::Responsive:
document->page()->setEventThrottlingBehaviorOverride(WebCore::EventThrottlingBehavior::Responsive);
break;
case Internals::EventThrottlingBehavior::Unresponsive:
document->page()->setEventThrottlingBehaviorOverride(WebCore::EventThrottlingBehavior::Unresponsive);
break;
}
}
std::optional<Internals::EventThrottlingBehavior> Internals::eventThrottlingBehaviorOverride() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return std::nullopt;
auto behavior = document->page()->eventThrottlingBehaviorOverride();
if (!behavior)
return std::nullopt;
switch (behavior.value()) {
case WebCore::EventThrottlingBehavior::Responsive:
return Internals::EventThrottlingBehavior::Responsive;
case WebCore::EventThrottlingBehavior::Unresponsive:
return Internals::EventThrottlingBehavior::Unresponsive;
}
return std::nullopt;
}
String Internals::visiblePlaceholder(Element& element)
{
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
if (auto* textFormControlElement = dynamicDowncast<HTMLTextFormControlElement>(element)) {
if (!textFormControlElement->isPlaceholderVisible())
return String();
if (RefPtr placeholderElement = textFormControlElement->placeholderElement())
return placeholderElement->textContent();
}
return String();
}
void Internals::setCanShowPlaceholder(Element& element, bool canShowPlaceholder)
{
if (auto* textFormControlElement = dynamicDowncast<HTMLTextFormControlElement>(element))
textFormControlElement->setCanShowPlaceholder(canShowPlaceholder);
}
RefPtr<Element> Internals::insertTextPlaceholder(int width, int height)
{
RefPtr localFrame = frame();
return localFrame ? localFrame->editor().insertTextPlaceholder(IntSize { width, height }) : nullptr;
}
void Internals::removeTextPlaceholder(Element& element)
{
if (auto* placeholderElement = dynamicDowncast<TextPlaceholderElement>(element))
frame()->editor().removeTextPlaceholder(*placeholderElement);
}
void Internals::selectColorInColorChooser(HTMLInputElement& element, const String& colorValue)
{
element.selectColor(colorValue);
}
ExceptionOr<Vector<AtomString>> Internals::formControlStateOfPreviousHistoryItem()
{
RefPtr mainItem = frame() ? frame()->loader().history().previousItem() : nullptr;
if (!mainItem)
return Exception { ExceptionCode::InvalidAccessError };
auto frameID = frame()->frameID();
if (mainItem->frameID() != frameID && !mainItem->childItemWithFrameID(frameID))
return Exception { ExceptionCode::InvalidAccessError };
return Vector<AtomString> { mainItem->frameID() == frameID ? mainItem->documentState() : mainItem->childItemWithFrameID(frameID)->documentState() };
}
ExceptionOr<void> Internals::setFormControlStateOfPreviousHistoryItem(const Vector<AtomString>& state)
{
RefPtr mainItem = frame() ? frame()->loader().history().previousItem() : nullptr;
if (!mainItem)
return Exception { ExceptionCode::InvalidAccessError };
auto frameID = frame()->frameID();
if (mainItem->frameID() == frameID)
mainItem->setDocumentState(state);
else if (HistoryItem* subItem = mainItem->childItemWithFrameID(frameID))
subItem->setDocumentState(state);
else
return Exception { ExceptionCode::InvalidAccessError };
return { };
}
#if ENABLE(SPEECH_SYNTHESIS)
void Internals::simulateSpeechSynthesizerVoiceListChange()
{
if (m_platformSpeechSynthesizer) {
m_platformSpeechSynthesizer->setInitialVoiceListToEmpty(false);
m_platformSpeechSynthesizer->initializeVoiceList();
m_platformSpeechSynthesizer->client().voicesDidChange();
return;
}
RefPtr document = contextDocument();
if (!document || !document->window())
return;
if (RefPtr synthesis = LocalDOMWindowSpeechSynthesis::speechSynthesis(*document->window()))
synthesis->simulateVoicesListChange();
}
void Internals::enableMockSpeechSynthesizer()
{
RefPtr document = contextDocument();
if (!document || !document->window())
return;
auto synthesis = LocalDOMWindowSpeechSynthesis::speechSynthesis(*document->window());
if (!synthesis)
return;
Ref mock = PlatformSpeechSynthesizerMock::create(*synthesis);
m_platformSpeechSynthesizer = mock.copyRef();
synthesis->setPlatformSynthesizer(WTF::move(mock));
}
void Internals::enableMockSpeechSynthesizerForMediaElement(HTMLMediaElement& element)
{
Ref synthesis = element.speechSynthesis();
Ref mock = PlatformSpeechSynthesizerMock::create(synthesis);
m_platformSpeechSynthesizer = mock.copyRef();
synthesis->setPlatformSynthesizer(WTF::move(mock));
}
void Internals::setInitialVoiceListToEmpty()
{
if (m_platformSpeechSynthesizer)
m_platformSpeechSynthesizer->setInitialVoiceListToEmpty(true);
}
ExceptionOr<void> Internals::setSpeechUtteranceDuration(double duration)
{
if (!m_platformSpeechSynthesizer)
return Exception { ExceptionCode::InvalidAccessError };
m_platformSpeechSynthesizer->setUtteranceDuration(Seconds(duration));
return { };
}
unsigned Internals::minimumExpectedVoiceCount()
{
// https://webkit.org/b/250656
#if PLATFORM(MAC)
return 21;
#elif USE(GLIB)
return 4;
#else
return 1;
#endif
}
#endif
#if ENABLE(WEB_RTC)
void Internals::useMockRTCPeerConnectionFactory(const String& testCase)
{
if (!WebRTCProvider::webRTCAvailable())
return;
#if USE(LIBWEBRTC)
RefPtr document = contextDocument();
auto* provider = (document && document->page()) ? &downcast<LibWebRTCProvider>(document->page()->webRTCProvider()) : nullptr;
WebCore::useMockRTCPeerConnectionFactory(provider, testCase);
#else
UNUSED_PARAM(testCase);
#endif
}
void Internals::setICECandidateFiltering(bool enabled)
{
auto* page = contextDocument()->page();
if (!page)
return;
auto& rtcController = page->rtcController();
if (enabled)
rtcController.enableICECandidateFiltering();
else
rtcController.disableICECandidateFilteringForAllOrigins();
}
void Internals::setEnumeratingAllNetworkInterfacesEnabled(bool enabled)
{
#if USE(LIBWEBRTC)
RefPtr document = contextDocument();
RefPtr page = document->page();
if (!page)
return;
auto& rtcProvider = downcast<LibWebRTCProvider>(page->webRTCProvider());
if (enabled)
rtcProvider.enableEnumeratingAllNetworkInterfaces();
else
rtcProvider.disableEnumeratingAllNetworkInterfaces();
#else
UNUSED_PARAM(enabled);
#endif
}
void Internals::stopPeerConnection(RTCPeerConnection& connection)
{
ActiveDOMObject& object = connection;
object.stop();
}
void Internals::clearPeerConnectionFactory()
{
if (RefPtr page = contextDocument()->page())
page->webRTCProvider().clearFactory();
}
void Internals::applyRotationForOutgoingVideoSources(RTCPeerConnection& connection)
{
connection.applyRotationForOutgoingVideoSources();
}
void Internals::setWebRTCH265Support(bool value)
{
if (RefPtr page = contextDocument()->page()) {
page->webRTCProvider().setH265Support(value);
page->webRTCProvider().clearFactory();
}
}
void Internals::setWebRTCVP9Support(bool supportVP9Profile0, bool supportVP9Profile2)
{
if (RefPtr page = contextDocument()->page()) {
page->webRTCProvider().setVP9Support(supportVP9Profile0, supportVP9Profile2);
page->webRTCProvider().clearFactory();
}
}
void Internals::disableWebRTCHardwareVP9()
{
#if USE(LIBWEBRTC)
if (RefPtr page = contextDocument()->page()) {
auto& rtcProvider = downcast<LibWebRTCProvider>(page->webRTCProvider());
rtcProvider.setVP9HardwareSupportForTesting(false);
rtcProvider.clearFactory();
}
#endif
}
bool Internals::isSupportingVP9HardwareDecoder() const
{
#if USE(LIBWEBRTC)
if (RefPtr page = contextDocument()->page()) {
auto& rtcProvider = downcast<LibWebRTCProvider>(page->webRTCProvider());
return rtcProvider.isSupportingVP9HardwareDecoder();
}
#endif
return false;
}
void Internals::isVP9HardwareDecoderUsed(RTCPeerConnection& connection, DOMPromiseDeferred<IDLBoolean>&& promise)
{
connection.gatherDecoderImplementationName([promise = WTF::move(promise)](auto&& name) mutable {
promise.resolve(!name.contains("fallback from:"_s) && !name.contains("libvpx"_s));
});
}
void Internals::setSFrameCounter(RTCRtpSFrameTransform& transform, const String& counter)
{
if (auto value = parseInteger<uint64_t>(counter))
transform.setCounterForTesting(*value);
}
uint64_t Internals::sframeCounter(const RTCRtpSFrameTransform& transform)
{
return transform.counterForTesting();
}
uint64_t Internals::sframeKeyId(const RTCRtpSFrameTransform& transform)
{
return transform.keyIdForTesting();
}
void Internals::setEnableWebRTCEncryption(bool value)
{
#if USE(LIBWEBRTC)
if (RefPtr page = contextDocument()->page())
page->settings().setWebRTCEncryptionEnabled(value);
#else
UNUSED_PARAM(value);
#endif
}
bool Internals::hasPeerConnectionEnabledServiceClass(const RTCPeerConnection& connection)
{
return connection.protectedBackend()->shouldEnableServiceClass();
}
#endif // ENABLE(WEB_RTC)
#if ENABLE(MEDIA_STREAM)
void Internals::setShouldInterruptAudioOnPageVisibilityChange(bool shouldInterrupt)
{
RefPtr document = contextDocument();
if (RefPtr page = document->page())
page->settings().setInterruptAudioOnPageVisibilityChangeEnabled(shouldInterrupt);
}
#endif // ENABLE(MEDIA_STREAM)
#if ENABLE(MEDIA_RECORDER)
static ExceptionOr<std::unique_ptr<MediaRecorderPrivate>> createRecorderMockSource(MediaStreamPrivate& stream, const MediaRecorderPrivateOptions&)
{
return std::unique_ptr<MediaRecorderPrivate>(new MediaRecorderPrivateMock(stream));
}
void Internals::setCustomPrivateRecorderCreator()
{
WebCore::MediaRecorder::setCustomPrivateRecorderCreator(createRecorderMockSource);
}
#endif // ENABLE(MEDIA_RECORDER)
ExceptionOr<Ref<DOMRect>> Internals::absoluteLineRectFromPoint(int x, int y)
{
if (!contextDocument() || !contextDocument()->page())
return Exception { ExceptionCode::InvalidAccessError };
Ref document = *contextDocument();
if (!document->frame() || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
Ref frame = *document->frame();
Ref view = *document->view();
document->updateLayout(LayoutOptions::IgnorePendingStylesheets);
auto position = frame->visiblePositionForPoint(view->rootViewToContents(IntPoint { x, y }));
return DOMRect::create(position.absoluteSelectionBoundsForLine());
}
ExceptionOr<Ref<DOMRect>> Internals::absoluteCaretBounds()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
return DOMRect::create(document->frame()->selection().absoluteCaretBounds());
}
ExceptionOr<bool> Internals::isCaretVisible()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
return document->frame()->selection().isCaretVisible();
}
ExceptionOr<bool> Internals::isCaretBlinkingSuspended()
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
return isCaretBlinkingSuspended(*document);
}
ExceptionOr<bool> Internals::isCaretBlinkingSuspended(Document& document)
{
if (!document.frame())
return Exception { ExceptionCode::InvalidAccessError };
return document.frame()->selection().isCaretBlinkingSuspended();
}
Ref<DOMRect> Internals::boundingBox(Element& element)
{
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
auto renderer = element.renderer();
if (!renderer)
return DOMRect::create();
return DOMRect::create(renderer->absoluteBoundingBoxRectIgnoringTransforms());
}
ExceptionOr<unsigned> Internals::inspectorGridOverlayCount()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
return document->page()->inspectorController().gridOverlayCount();
}
ExceptionOr<unsigned> Internals::inspectorFlexOverlayCount()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
return document->page()->inspectorController().flexOverlayCount();
}
ExceptionOr<Ref<DOMRectList>> Internals::inspectorHighlightRects()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
InspectorOverlay::Highlight highlight;
document->page()->inspectorController().getHighlight(highlight, InspectorOverlay::CoordinateSystem::View);
return DOMRectList::create(highlight.quads);
}
ExceptionOr<unsigned> Internals::inspectorPaintRectCount()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
return document->page()->inspectorController().paintRectCount();
}
ExceptionOr<unsigned> Internals::markerCountForNode(Node& node, const String& markerType)
{
OptionSet<DocumentMarkerType> markerTypes;
if (!markerTypesFrom(markerType, markerTypes))
return Exception { ExceptionCode::SyntaxError };
node.document().editor().updateEditorUINowIfScheduled();
return node.document().markers().markersFor(node, markerTypes).size();
}
ExceptionOr<RenderedDocumentMarker*> Internals::markerAt(Node& node, const String& markerType, unsigned index)
{
node.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
OptionSet<DocumentMarkerType> markerTypes;
if (!markerTypesFrom(markerType, markerTypes))
return Exception { ExceptionCode::SyntaxError };
node.document().editor().updateEditorUINowIfScheduled();
auto markers = node.document().markers().markersFor(node, markerTypes);
if (markers.size() <= index)
return nullptr;
return markers[index].get();
}
ExceptionOr<RefPtr<Range>> Internals::markerRangeForNode(Node& node, const String& markerType, unsigned index)
{
auto result = markerAt(node, markerType, index);
if (result.hasException())
return result.releaseException();
auto marker = result.releaseReturnValue();
if (!marker)
return nullptr;
return { createLiveRange(makeSimpleRange(node, *marker)) };
}
ExceptionOr<String> Internals::markerDescriptionForNode(Node& node, const String& markerType, unsigned index)
{
auto result = markerAt(node, markerType, index);
if (result.hasException())
return result.releaseException();
auto marker = result.releaseReturnValue();
if (!marker)
return String();
return String { marker->description() };
}
ExceptionOr<String> Internals::dumpMarkerRects(const String& markerTypeString)
{
DocumentMarkerType markerType;
if (!markerTypeFrom(markerTypeString, markerType))
return Exception { ExceptionCode::SyntaxError };
contextDocument()->markers().updateRectsForInvalidatedMarkersOfType(markerType);
auto rects = contextDocument()->markers().renderedRectsForMarkers(markerType);
// FIXME: Using fixed precision here for width because of test results that contain numbers with specific precision. Would be nice to update the test results and move to default formatting.
StringBuilder rectString;
rectString.append("marker rects: "_s);
for (const auto& rect : rects)
rectString.append('(', rect.x(), ", "_s, rect.y(), ", "_s, FormattedNumber::fixedPrecision(rect.width()), ", "_s, rect.height(), ") "_s);
return String { rectString.toString() };
}
ExceptionOr<void> Internals::setMarkedTextMatchesAreHighlighted(bool flag)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->editor().setMarkedTextMatchesAreHighlighted(flag);
return { };
}
ExceptionOr<RefPtr<ImageData>> Internals::snapshotNode(Node& node)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->updateLayoutIgnorePendingStylesheets();
SnapshotOptions options { { SnapshotFlags::DraggableElement }, PixelFormat::BGRA8, DestinationColorSpace::SRGB() };
RefPtr imageBuffer = WebCore::snapshotNode(*document->frame(), node, WTF::move(options));
if (!imageBuffer)
return Exception { ExceptionCode::InvalidStateError, "Failed to create snapshot"_s };
auto size = imageBuffer->logicalSize();
IntRect sourceRect(IntPoint(), imageBuffer->calculateBackendSize(size,
document->frame()->page()->deviceScaleFactor()));
PixelBufferFormat destinationFormat {
AlphaPremultiplication::Unpremultiplied,
PixelFormat::RGBA8,
DestinationColorSpace::SRGB()
};
auto pixelBuffer = imageBuffer->getPixelBuffer(destinationFormat, sourceRect);
if (!pixelBuffer)
return Exception { ExceptionCode::InvalidStateError, "Failed to get pixel buffer"_s };
auto byteArrayPixelBuffer = dynamicDowncast<ByteArrayPixelBuffer>(pixelBuffer.get());
if (!byteArrayPixelBuffer)
return Exception { ExceptionCode::InvalidStateError, "Pixel buffer is not a ByteArrayPixelBuffer"_s };
RefPtr imageData = ImageData::create(WTF::move(byteArrayPixelBuffer));
if (!imageData)
return Exception { ExceptionCode::InvalidStateError, "Failed to create ImageData"_s };
return imageData;
}
void Internals::invalidateFontCache()
{
FontCache::invalidateAllFontCaches();
}
ExceptionOr<void> Internals::setLowPowerModeEnabled(bool isEnabled)
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
RefPtr page = document->page();
if (!page)
return Exception { ExceptionCode::InvalidAccessError };
page->setLowPowerModeEnabledOverrideForTesting(isEnabled);
return { };
}
ExceptionOr<void> Internals::setAggressiveThermalMitigationEnabled(bool isEnabled)
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
RefPtr page = document->page();
if (!page)
return Exception { ExceptionCode::InvalidAccessError };
page->setAggressiveThermalMitigationEnabledForTesting(isEnabled);
return { };
}
ExceptionOr<void> Internals::setOutsideViewportThrottlingEnabled(bool isEnabled)
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
RefPtr page = document->page();
if (!page)
return Exception { ExceptionCode::InvalidAccessError };
page->setOutsideViewportThrottlingEnabledForTesting(isEnabled);
return { };
}
ExceptionOr<void> Internals::setScrollViewPosition(int x, int y)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
Ref frameView = *document->view();
auto oldClamping = frameView->scrollClamping();
bool scrollbarsSuppressedOldValue = frameView->scrollbarsSuppressed();
frameView->setScrollClamping(ScrollClamping::Unclamped);
frameView->setScrollbarsSuppressed(false);
frameView->setScrollOffsetFromInternals({ x, y });
frameView->setScrollbarsSuppressed(scrollbarsSuppressedOldValue);
frameView->setScrollClamping(oldClamping);
return { };
}
ExceptionOr<void> Internals::unconstrainedScrollTo(Element& element, double x, double y)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
element.scrollTo(ScrollToOptions { { ScrollBehavior::Auto }, x, y }, ScrollClamping::Unclamped);
Ref frameView = *document->view();
frameView->setViewportConstrainedObjectsNeedLayout();
return { };
}
ExceptionOr<void> Internals::scrollBySimulatingWheelEvent(Element& element, double deltaX, double deltaY)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
if (!element.renderBox())
return Exception { ExceptionCode::InvalidAccessError };
RenderBox& box = *element.renderBox();
ScrollableArea* scrollableArea;
if (&element == document->scrollingElementForAPI()) {
auto* localFrame = dynamicDowncast<LocalFrame>(box.frame().mainFrame());
if (!localFrame)
return Exception { ExceptionCode::InvalidAccessError };
auto* frameView = localFrame->view();
if (!frameView || !frameView->isScrollable())
return Exception { ExceptionCode::InvalidAccessError };
scrollableArea = frameView;
} else {
if (!box.canBeScrolledAndHasScrollableArea())
return Exception { ExceptionCode::InvalidAccessError };
ASSERT(box.layer());
scrollableArea = box.layer()->scrollableArea();
}
if (!scrollableArea)
return Exception { ExceptionCode::InvalidAccessError };
auto scrollingNodeID = scrollableArea->scrollingNodeID();
if (!scrollingNodeID)
return Exception { ExceptionCode::InvalidAccessError };
RefPtr page = document->page();
if (!page)
return Exception { ExceptionCode::InvalidAccessError };
RefPtr scrollingCoordinator = page->scrollingCoordinator();
if (!scrollingCoordinator)
return Exception { ExceptionCode::InvalidAccessError };
scrollingCoordinator->scrollBySimulatingWheelEventForTesting(*scrollingNodeID, FloatSize(deltaX, deltaY));
return { };
}
ExceptionOr<Ref<DOMRect>> Internals::layoutViewportRect()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->updateLayout(LayoutOptions::IgnorePendingStylesheets);
Ref frameView = *document->view();
return DOMRect::create(frameView->layoutViewportRect());
}
ExceptionOr<Ref<DOMRect>> Internals::visualViewportRect()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->updateLayout(LayoutOptions::IgnorePendingStylesheets);
Ref frameView = *document->view();
return DOMRect::create(frameView->visualViewportRect());
}
ExceptionOr<void> Internals::setViewIsTransparent(bool transparent)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->setTransparent(transparent);
return { };
}
ExceptionOr<String> Internals::viewBaseBackgroundColor()
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->updateLayout(LayoutOptions::IgnorePendingStylesheets);
return serializationForCSS(document->view()->baseBackgroundColor());
}
ExceptionOr<void> Internals::setViewBaseBackgroundColor(const String& colorValue)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
if (colorValue == "transparent"_s) {
document->view()->setBaseBackgroundColor(Color::transparentBlack);
return { };
}
if (colorValue == "white"_s) {
document->view()->setBaseBackgroundColor(Color::white);
return { };
}
return Exception { ExceptionCode::SyntaxError };
}
ExceptionOr<void> Internals::setUnderPageBackgroundColorOverride(const String& colorValue)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
auto cssParserContext = document->cssParserContext();
auto color = CSSPropertyParserHelpers::parseColorRaw(colorValue, cssParserContext, *document);
if (!color.isValid())
return Exception { ExceptionCode::SyntaxError };
document->page()->setUnderPageBackgroundColorOverride(WTF::move(color));
return { };
}
ExceptionOr<String> Internals::documentBackgroundColor()
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
return serializationForCSS(document->view()->documentBackgroundColor());
}
ExceptionOr<void> Internals::setPagination(const String& mode, int gap, int pageLength)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
Pagination pagination;
if (mode == "Unpaginated"_s)
pagination.mode = Pagination::Mode::Unpaginated;
else if (mode == "LeftToRightPaginated"_s)
pagination.mode = Pagination::Mode::LeftToRightPaginated;
else if (mode == "RightToLeftPaginated"_s)
pagination.mode = Pagination::Mode::RightToLeftPaginated;
else if (mode == "TopToBottomPaginated"_s)
pagination.mode = Pagination::Mode::TopToBottomPaginated;
else if (mode == "BottomToTopPaginated"_s)
pagination.mode = Pagination::Mode::BottomToTopPaginated;
else
return Exception { ExceptionCode::SyntaxError };
pagination.gap = gap;
pagination.pageLength = pageLength;
document->page()->setPagination(pagination);
return { };
}
ExceptionOr<uint64_t> Internals::lineIndexAfterPageBreak(Element& element)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
if (!element.renderer() || !is<RenderBlockFlow>(element.renderer()))
return Exception { ExceptionCode::NotFoundError };
auto& blockFlow = downcast<RenderBlockFlow>(*element.renderer());
if (!blockFlow.childrenInline())
return Exception { ExceptionCode::NotFoundError };
size_t lineIndex = 0;
for (auto lineBox = InlineIterator::firstLineBoxFor(blockFlow); lineBox; lineBox.traverseNext(), ++lineIndex) {
if (lineBox->isFirstAfterPageBreak())
return lineIndex;
}
return Exception { ExceptionCode::NotFoundError };
}
ExceptionOr<String> Internals::configurationForViewport(float devicePixelRatio, int deviceWidth, int deviceHeight, int availableWidth, int availableHeight)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
const int defaultLayoutWidthForNonMobilePages = 980;
ViewportArguments arguments = document->page()->viewportArguments();
ViewportAttributes attributes = computeViewportAttributes(arguments, defaultLayoutWidthForNonMobilePages, deviceWidth, deviceHeight, devicePixelRatio, IntSize(availableWidth, availableHeight));
restrictMinimumScaleFactorToViewportSize(attributes, IntSize(availableWidth, availableHeight), devicePixelRatio);
restrictScaleFactorToInitialScaleIfNotUserScalable(attributes);
// FIXME: Using fixed precision here because of test results that contain numbers with specific precision. Would be nice to update the test results and move to default formatting.
return makeString("viewport size "_s, FormattedNumber::fixedPrecision(attributes.layoutSize.width()), 'x', FormattedNumber::fixedPrecision(attributes.layoutSize.height()), " scale "_s, FormattedNumber::fixedPrecision(attributes.initialScale), " with limits ["_s, FormattedNumber::fixedPrecision(attributes.minimumScale), ", "_s, FormattedNumber::fixedPrecision(attributes.maximumScale), "] and userScalable "_s, (attributes.userScalable ? "true"_s : "false"_s));
}
ExceptionOr<bool> Internals::wasLastChangeUserEdit(Element& textField)
{
if (auto* input = dynamicDowncast<HTMLInputElement>(textField))
return input->lastChangeWasUserEdit();
if (auto* textArea = dynamicDowncast<HTMLTextAreaElement>(textField))
return textArea->lastChangeWasUserEdit();
return Exception { ExceptionCode::InvalidNodeTypeError };
}
bool Internals::elementShouldAutoComplete(HTMLInputElement& element)
{
return element.shouldAutocomplete();
}
void Internals::setAutofilled(HTMLInputElement& element, bool enabled)
{
element.setAutofilled(enabled);
}
void Internals::setAutofilledAndViewable(HTMLInputElement& element, bool enabled)
{
element.setAutofilledAndViewable(enabled);
}
void Internals::setAutofilledAndObscured(HTMLInputElement& element, bool enabled)
{
element.setAutofilledAndObscured(enabled);
}
static AutoFillButtonType toAutofillButtonType(Internals::AutoFillButtonType type)
{
switch (type) {
case Internals::AutoFillButtonType::None:
return AutoFillButtonType::None;
case Internals::AutoFillButtonType::Credentials:
return AutoFillButtonType::Credentials;
case Internals::AutoFillButtonType::Contacts:
return AutoFillButtonType::Contacts;
case Internals::AutoFillButtonType::StrongPassword:
return AutoFillButtonType::StrongPassword;
case Internals::AutoFillButtonType::CreditCard:
return AutoFillButtonType::CreditCard;
case Internals::AutoFillButtonType::Loading:
return AutoFillButtonType::Loading;
}
ASSERT_NOT_REACHED();
return AutoFillButtonType::None;
}
static Internals::AutoFillButtonType toInternalsAutofillButtonType(AutoFillButtonType type)
{
switch (type) {
case AutoFillButtonType::None:
return Internals::AutoFillButtonType::None;
case AutoFillButtonType::Credentials:
return Internals::AutoFillButtonType::Credentials;
case AutoFillButtonType::Contacts:
return Internals::AutoFillButtonType::Contacts;
case AutoFillButtonType::StrongPassword:
return Internals::AutoFillButtonType::StrongPassword;
case AutoFillButtonType::CreditCard:
return Internals::AutoFillButtonType::CreditCard;
case AutoFillButtonType::Loading:
return Internals::AutoFillButtonType::Loading;
}
ASSERT_NOT_REACHED();
return Internals::AutoFillButtonType::None;
}
void Internals::setAutofillButtonType(HTMLInputElement& element, AutoFillButtonType type)
{
element.setAutofillButtonType(toAutofillButtonType(type));
}
auto Internals::autofillButtonType(const HTMLInputElement& element) -> AutoFillButtonType
{
return toInternalsAutofillButtonType(element.autofillButtonType());
}
auto Internals::lastAutofillButtonType(const HTMLInputElement& element) -> AutoFillButtonType
{
return toInternalsAutofillButtonType(element.lastAutofillButtonType());
}
Vector<String> Internals::recentSearches(const HTMLInputElement& element)
{
if (!element.isSearchField())
return { };
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
auto* searchField = dynamicDowncast<RenderSearchField>(element.renderer());
if (!searchField)
return { };
Vector<String> result;
for (auto search : searchField->recentSearches())
result.append(search.string);
return result;
}
ExceptionOr<void> Internals::scrollElementToRect(Element& element, int x, int y, int w, int h)
{
auto* frameView = element.document().view();
if (!frameView)
return Exception { ExceptionCode::InvalidAccessError };
frameView->scrollElementToRect(element, { x, y, w, h });
return { };
}
ExceptionOr<String> Internals::autofillFieldName(Element& element)
{
if (auto* formControl = dynamicDowncast<HTMLFormControlElement>(element))
return String { formControl->autofillData().fieldName };
return Exception { ExceptionCode::InvalidNodeTypeError };
}
ExceptionOr<void> Internals::invalidateControlTints()
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->invalidateControlTints();
return { };
}
RefPtr<Range> Internals::rangeFromLocationAndLength(Element& scope, unsigned rangeLocation, unsigned rangeLength)
{
return createLiveRange(resolveCharacterRange(makeRangeSelectingNodeContents(scope), { rangeLocation, rangeLength }));
}
unsigned Internals::locationFromRange(Element& scope, const Range& range)
{
return clampTo<unsigned>(characterRange(makeBoundaryPointBeforeNodeContents(scope), makeSimpleRange(range)).location);
}
unsigned Internals::lengthFromRange(Element& scope, const Range& range)
{
return clampTo<unsigned>(characterRange(makeBoundaryPointBeforeNodeContents(scope), makeSimpleRange(range)).length);
}
String Internals::rangeAsText(const Range& liveRange)
{
auto range = makeSimpleRange(liveRange);
range.start.document().updateLayout();
return plainText(range);
}
// FIXME: Move this to StringConcatenate.h.
static String join(Vector<String>&& strings)
{
StringBuilder result;
for (auto& string : strings)
result.append(WTF::move(string));
return result.toString();
}
String Internals::rangeAsTextUsingBackwardsTextIterator(const Range& liveRange)
{
auto range = makeSimpleRange(liveRange);
range.start.document().updateLayout();
Vector<String> strings;
for (SimplifiedBackwardsTextIterator backwardsIterator(range); !backwardsIterator.atEnd(); backwardsIterator.advance())
strings.append(backwardsIterator.text().toString());
strings.reverse();
return join(WTF::move(strings));
}
Ref<Range> Internals::subrange(Range& liveRange, unsigned rangeLocation, unsigned rangeLength)
{
auto range = makeSimpleRange(liveRange);
range.start.document().updateLayout();
return createLiveRange(resolveCharacterRange(range, { rangeLocation, rangeLength }));
}
RefPtr<Range> Internals::rangeOfStringNearLocation(const Range& liveRange, const String& text, unsigned targetOffset)
{
auto range = makeSimpleRange(liveRange);
range.start.document().updateLayout();
return createLiveRange(findClosestPlainText(range, text, { }, targetOffset));
}
Vector<Internals::TextIteratorState> Internals::statesOfTextIterator(const Range& liveRange)
{
auto simpleRange = makeSimpleRange(liveRange);
simpleRange.start.document().updateLayout();
Vector<TextIteratorState> states;
for (TextIterator it(simpleRange); !it.atEnd(); it.advance())
states.append({ it.text().toString(), createLiveRange(it.range()) });
return states;
}
String Internals::textFragmentDirectiveForRange(const Range& range)
{
auto simpleRange = makeSimpleRange(range);
simpleRange.start.document().updateLayout();
return FragmentDirectiveGenerator(simpleRange).urlWithFragment().string();
}
#if !PLATFORM(MAC)
ExceptionOr<RefPtr<Range>> Internals::rangeForDictionaryLookupAtLocation(int, int)
{
return Exception { ExceptionCode::InvalidAccessError };
}
#endif
ExceptionOr<void> Internals::setDelegatesScrolling(bool enabled)
{
RefPtr document = contextDocument();
// Delegate scrolling is valid only on mainframe's view.
if (!document || !document->view() || !document->page() || &document->page()->mainFrame() != document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->setDelegatedScrollingMode(enabled ? DelegatedScrollingMode::DelegatedToNativeScrollView : DelegatedScrollingMode::NotDelegated);
return { };
}
ExceptionOr<uint64_t> Internals::lastSpellCheckRequestSequence()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
auto lastRequestIdentifier = document->editor().spellChecker().lastRequestIdentifier();
return lastRequestIdentifier ? lastRequestIdentifier->toUInt64() : 0;
}
ExceptionOr<uint64_t> Internals::lastSpellCheckProcessedSequence()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
auto lastProcessedIdentifier = document->editor().spellChecker().lastProcessedIdentifier();
return lastProcessedIdentifier ? lastProcessedIdentifier->toUInt64() : 0;
}
void Internals::advanceToNextMisspelling()
{
#if !PLATFORM(IOS_FAMILY)
if (RefPtr document = contextDocument())
document->editor().advanceToNextMisspelling();
#endif
}
Vector<String> Internals::userPreferredLanguages() const
{
return WTF::userPreferredLanguages(ShouldMinimizeLanguages::No);
}
void Internals::setUserPreferredLanguages(const Vector<String>& languages)
{
overrideUserPreferredLanguages(languages);
}
Vector<String> Internals::userPreferredAudioCharacteristics() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Vector<String>();
#if ENABLE(VIDEO)
return document->page()->group().ensureCaptionPreferences().preferredAudioCharacteristics();
#else
return Vector<String>();
#endif
}
void Internals::setUserPreferredAudioCharacteristic(const String& characteristic)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return;
#if ENABLE(VIDEO)
document->page()->group().ensureCaptionPreferences().setPreferredAudioCharacteristic(characteristic);
#else
UNUSED_PARAM(characteristic);
#endif
}
ExceptionOr<unsigned> Internals::wheelEventHandlerCount()
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
return document->wheelEventHandlerCount();
}
ExceptionOr<unsigned> Internals::touchEventHandlerCount()
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
return document->touchEventHandlerCount();
}
ExceptionOr<Ref<DOMRectList>> Internals::touchEventRectsForEvent(const String& eventName)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
std::array<EventTrackingRegions::EventType, 4> touchEvents = { {
EventTrackingRegions::EventType::Touchstart,
EventTrackingRegions::EventType::Touchmove,
EventTrackingRegions::EventType::Touchend,
EventTrackingRegions::EventType::Touchforcechange,
} };
std::optional<EventTrackingRegions::EventType> touchEvent;
for (auto event : touchEvents) {
if (eventName == EventTrackingRegions::eventName(event)) {
touchEvent = event;
break;
}
}
if (!touchEvent)
return Exception { ExceptionCode::InvalidAccessError };
return document->page()->touchEventRectsForEventForTesting(touchEvent.value());
}
ExceptionOr<Ref<DOMRectList>> Internals::passiveTouchEventListenerRects()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
return document->page()->passiveTouchEventListenerRectsForTesting();
}
// FIXME: Remove the document argument. It is almost always the same as
// contextDocument(), with the exception of a few tests that pass a
// different document, and could just make the call through another Internals
// instance instead.
ExceptionOr<RefPtr<NodeList>> Internals::nodesFromRect(Document& document, int centerX, int centerY, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding, bool ignoreClipping, bool allowUserAgentShadowContent, bool allowChildFrameContent) const
{
if (!document.frame() || !document.frame()->view())
return Exception { ExceptionCode::InvalidAccessError };
RefPtr frame = document.frame();
auto* frameView = document.view();
auto* renderView = document.renderView();
if (!renderView)
return nullptr;
document.updateLayout(LayoutOptions::IgnorePendingStylesheets);
float zoomFactor = frame->pageZoomFactor();
LayoutPoint point(centerX * zoomFactor + frameView->scrollX(), centerY * zoomFactor + frameView->scrollY());
OptionSet<HitTestRequest::Type> hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::CollectMultipleElements };
if (ignoreClipping)
hitType.add(HitTestRequest::Type::IgnoreClipping);
if (!allowUserAgentShadowContent)
hitType.add(HitTestRequest::Type::DisallowUserAgentShadowContent);
if (allowChildFrameContent)
hitType.add(HitTestRequest::Type::AllowChildFrameContent);
HitTestRequest request(hitType);
auto hitTestResult = [&] {
auto size = LayoutSize { leftPadding + rightPadding + 1, topPadding + bottomPadding + 1 };
if (size.isEmpty())
return HitTestResult { point };
auto adjustedPosition = LayoutPoint { flooredIntPoint(point) } - LayoutSize { leftPadding, topPadding };
return HitTestResult { LayoutRect { adjustedPosition, size } };
}();
// When ignoreClipping is false, this method returns null for coordinates outside of the viewport.
if (!request.ignoreClipping() && !hitTestResult.hitTestLocation().intersects(LayoutRect { frameView->visibleContentRect() }))
return nullptr;
document.hitTest(request, hitTestResult);
auto matches = WTF::map(hitTestResult.listBasedTestResult(), [](const auto& node) { return node.copyRef(); });
return RefPtr<NodeList> { StaticNodeList::create(WTF::move(matches)) };
}
class GetCallerCodeBlockFunctor {
public:
GetCallerCodeBlockFunctor()
: m_iterations(0)
, m_codeBlock(0)
{
}
IterationStatus operator()(StackVisitor& visitor) const
{
++m_iterations;
if (m_iterations < 2)
return IterationStatus::Continue;
m_codeBlock = visitor->codeBlock();
return IterationStatus::Done;
}
CodeBlock* codeBlock() const { return m_codeBlock; }
private:
mutable int m_iterations;
mutable CodeBlock* m_codeBlock;
};
String Internals::parserMetaData(JSC::JSValue code)
{
auto& vm = contextDocument()->vm();
auto callFrame = vm.topCallFrame;
auto* globalObject = callFrame->lexicalGlobalObject(vm);
ScriptExecutable* executable;
if (!code || code.isNull() || code.isUndefined()) {
GetCallerCodeBlockFunctor iter;
StackVisitor::visit(callFrame, vm, iter);
executable = iter.codeBlock()->ownerExecutable();
} else if (code.isCallable())
executable = JSC::jsCast<JSFunction*>(code.toObject(globalObject))->jsExecutable();
else
return String();
ASCIILiteral prefix;
String functionName;
ASCIILiteral suffix = ""_s;
if (auto* functionExecutable = jsDynamicCast<FunctionExecutable*>(executable)) {
prefix = "function \""_s;
functionName = functionExecutable->ecmaName().string();
suffix = "\""_s;
} else if (executable->isEvalExecutable())
prefix = "eval"_s;
else if (executable->isModuleProgramExecutable())
prefix = "module"_s;
else if (executable->isProgramExecutable())
prefix = "program"_s;
else
RELEASE_ASSERT_NOT_REACHED();
return makeString(prefix, functionName, suffix, " { "_s,
executable->firstLine(), ':', executable->startColumn(), " - "_s,
executable->lastLine(), ':', executable->endColumn(), " }"_s);
}
void Internals::updateEditorUINowIfScheduled()
{
if (Document* document = contextDocument()) {
if (LocalFrame* frame = document->frame())
frame->editor().updateEditorUINowIfScheduled();
}
}
bool Internals::hasMarkerFor(DocumentMarkerType type, int from, int length)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return false;
updateEditorUINowIfScheduled();
return document->editor().selectionStartHasMarkerFor(type, from, length);
}
ExceptionOr<void> Internals::setMarkerFor(const String& markerTypeString, int from, int length, const String& data)
{
DocumentMarkerType markerType;
if (!markerTypeFrom(markerTypeString, markerType))
return Exception { ExceptionCode::SyntaxError };
RefPtr document = contextDocument();
if (!document || !document->frame())
return { };
updateEditorUINowIfScheduled();
document->editor().selectionStartSetMarkerForTesting(markerType, from, length, data);
return { };
}
bool Internals::hasSpellingMarker(int from, int length)
{
return hasMarkerFor(DocumentMarkerType::Spelling, from, length);
}
bool Internals::hasGrammarMarker(int from, int length)
{
return hasMarkerFor(DocumentMarkerType::Grammar, from, length);
}
bool Internals::hasAutocorrectedMarker(int from, int length)
{
return hasMarkerFor(DocumentMarkerType::Autocorrected, from, length);
}
bool Internals::hasDictationAlternativesMarker(int from, int length)
{
return hasMarkerFor(DocumentMarkerType::DictationAlternatives, from, length);
}
bool Internals::hasCorrectionIndicatorMarker(int from, int length)
{
return hasMarkerFor(DocumentMarkerType::CorrectionIndicator, from, length);
}
#if ENABLE(WRITING_TOOLS)
bool Internals::hasWritingToolsTextSuggestionMarker(int from, int length)
{
return hasMarkerFor(DocumentMarkerType::WritingToolsTextSuggestion, from, length);
}
#endif
bool Internals::hasTransparentContentMarker(int from, int length)
{
return hasMarkerFor(DocumentMarkerType::TransparentContent, from, length);
}
void Internals::setContinuousSpellCheckingEnabled(bool enabled)
{
if (!contextDocument() || !contextDocument()->frame())
return;
if (enabled != contextDocument()->editor().isContinuousSpellCheckingEnabled())
contextDocument()->editor().toggleContinuousSpellChecking();
}
void Internals::setAutomaticQuoteSubstitutionEnabled(bool enabled)
{
if (!contextDocument() || !contextDocument()->frame())
return;
#if USE(AUTOMATIC_TEXT_REPLACEMENT)
if (enabled != contextDocument()->editor().isAutomaticQuoteSubstitutionEnabled())
contextDocument()->editor().toggleAutomaticQuoteSubstitution();
#else
UNUSED_PARAM(enabled);
#endif
}
void Internals::setAutomaticLinkDetectionEnabled(bool enabled)
{
if (!contextDocument() || !contextDocument()->frame())
return;
#if USE(AUTOMATIC_TEXT_REPLACEMENT)
if (enabled != contextDocument()->editor().isAutomaticLinkDetectionEnabled())
contextDocument()->editor().toggleAutomaticLinkDetection();
#else
UNUSED_PARAM(enabled);
#endif
}
ExceptionOr<bool> Internals::testProcessIncomingSyncMessagesWhenWaitingForSyncReply()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidStateError };
return document->page()->chrome().client().testProcessIncomingSyncMessagesWhenWaitingForSyncReply();
}
void Internals::setAutomaticDashSubstitutionEnabled(bool enabled)
{
if (!contextDocument() || !contextDocument()->frame())
return;
#if USE(AUTOMATIC_TEXT_REPLACEMENT)
if (enabled != contextDocument()->editor().isAutomaticDashSubstitutionEnabled())
contextDocument()->editor().toggleAutomaticDashSubstitution();
#else
UNUSED_PARAM(enabled);
#endif
}
void Internals::setAutomaticTextReplacementEnabled(bool enabled)
{
if (!contextDocument() || !contextDocument()->frame())
return;
#if USE(AUTOMATIC_TEXT_REPLACEMENT)
if (enabled != contextDocument()->editor().isAutomaticTextReplacementEnabled())
contextDocument()->editor().toggleAutomaticTextReplacement();
#else
UNUSED_PARAM(enabled);
#endif
}
void Internals::setAutomaticSpellingCorrectionEnabled(bool enabled)
{
if (!contextDocument() || !contextDocument()->frame())
return;
#if USE(AUTOMATIC_TEXT_REPLACEMENT)
if (enabled != contextDocument()->editor().isAutomaticSpellingCorrectionEnabled())
contextDocument()->editor().toggleAutomaticSpellingCorrection();
#else
UNUSED_PARAM(enabled);
#endif
}
bool Internals::isSpellcheckDisabledExceptTextReplacement(const HTMLInputElement& element) const
{
return element.isSpellcheckDisabledExceptTextReplacement();
}
void Internals::handleAcceptedCandidate(const String& candidate, unsigned location, unsigned length)
{
if (!contextDocument() || !contextDocument()->frame())
return;
TextCheckingResult result;
result.type = TextCheckingType::None;
result.range = { location, length };
result.replacement = candidate;
contextDocument()->editor().handleAcceptedCandidate(result);
}
void Internals::changeSelectionListType()
{
if (RefPtr frame = this->frame())
frame->editor().changeSelectionListType();
}
void Internals::changeBackToReplacedString(const String& replacedString)
{
if (RefPtr frame = this->frame())
frame->editor().changeBackToReplacedString(replacedString);
}
bool Internals::isOverwriteModeEnabled()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return false;
return document->editor().isOverwriteModeEnabled();
}
void Internals::toggleOverwriteModeEnabled()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return;
document->editor().toggleOverwriteModeEnabled();
}
static ExceptionOr<FindOptions> parseFindOptions(const Vector<String>& optionList)
{
struct FlagListEntry {
ASCIILiteral name;
FindOption value;
};
static constexpr auto flagList = std::to_array<FlagListEntry>({
{ "CaseInsensitive"_s, FindOption::CaseInsensitive },
{ "AtWordStarts"_s, FindOption::AtWordStarts },
{ "TreatMedialCapitalAsWordStart"_s, FindOption::TreatMedialCapitalAsWordStart },
{ "Backwards"_s, FindOption::Backwards },
{ "WrapAround"_s, FindOption::WrapAround },
{ "StartInSelection"_s, FindOption::StartInSelection },
{ "DoNotRevealSelection"_s, FindOption::DoNotRevealSelection },
{ "AtWordEnds"_s, FindOption::AtWordEnds },
{ "DoNotTraverseFlatTree"_s, FindOption::DoNotTraverseFlatTree },
});
FindOptions result;
for (auto& option : optionList) {
bool found = false;
for (auto& flag : flagList) {
if (flag.name == option) {
result.add(flag.value);
found = true;
break;
}
}
if (!found)
return Exception { ExceptionCode::SyntaxError };
}
return result;
}
ExceptionOr<RefPtr<Range>> Internals::rangeOfString(const String& text, RefPtr<Range>&& referenceRange, const Vector<String>& findOptions)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
auto parsedOptions = parseFindOptions(findOptions);
if (parsedOptions.hasException())
return parsedOptions.releaseException();
return createLiveRange(document->editor().rangeOfString(text, makeSimpleRange(referenceRange), parsedOptions.releaseReturnValue()));
}
ExceptionOr<unsigned> Internals::countMatchesForText(const String& text, const Vector<String>& findOptions, const String& markMatches)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
auto parsedOptions = parseFindOptions(findOptions);
if (parsedOptions.hasException())
return parsedOptions.releaseException();
bool mark = markMatches == "mark"_s;
return document->editor().countMatchesForText(text, std::nullopt, parsedOptions.releaseReturnValue(), 1000, mark, nullptr);
}
ExceptionOr<unsigned> Internals::countFindMatches(const String& text, const Vector<String>& findOptions)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
auto parsedOptions = parseFindOptions(findOptions);
if (parsedOptions.hasException())
return parsedOptions.releaseException();
return document->page()->countFindMatches(text, parsedOptions.releaseReturnValue(), 1000);
}
unsigned Internals::numberOfIDBTransactions() const
{
return IDBTransaction::numberOfIDBTransactions;
}
unsigned Internals::numberOfLiveNodes() const
{
unsigned nodeCount = 0;
for (auto& document : Document::allDocuments())
nodeCount += document->referencingNodeCount();
return nodeCount;
}
unsigned Internals::numberOfLiveDocuments() const
{
return Document::allDocuments().size();
}
unsigned Internals::referencingNodeCount(const Document& document) const
{
return document.referencingNodeCount();
}
ExceptionOr<void> Internals::executeOpportunisticallyScheduledTasks() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
document->page()->performOpportunisticallyScheduledTasks(MonotonicTime::now());
return { };
}
#if ENABLE(WEB_AUDIO)
uint64_t Internals::baseAudioContextIdentifier(const BaseAudioContext& context)
{
return context.contextID();
}
bool Internals::isBaseAudioContextAlive(uint64_t contextID)
{
ASSERT(contextID);
return BaseAudioContext::isContextAlive(contextID);
}
#endif // ENABLE(WEB_AUDIO)
unsigned Internals::numberOfIntersectionObservers(const Document& document) const
{
return document.numberOfIntersectionObservers();
}
unsigned Internals::numberOfResizeObservers(const Document& document) const
{
return document.numberOfResizeObservers();
}
String Internals::documentIdentifier(const Document& document) const
{
return document.identifier().object().toString();
}
ExceptionOr<bool> Internals::isDocumentAlive(const String& documentIdentifier) const
{
auto uuid = WTF::UUID::parseVersion4(documentIdentifier);
if (!uuid)
return Exception { ExceptionCode::InvalidAccessError };
return Document::allDocumentsMap().contains({ *uuid, Process::identifier() });
}
uint64_t Internals::messagePortIdentifier(const MessagePort& port) const
{
return port.identifier().portIdentifier.toUInt64();
}
bool Internals::isMessagePortAlive(uint64_t messagePortIdentifier) const
{
MessagePortIdentifier portIdentifier { Process::identifier(), AtomicObjectIdentifier<PortIdentifierType>(messagePortIdentifier) };
return MessagePort::isMessagePortAliveForTesting(portIdentifier);
}
uint64_t Internals::storageAreaMapCount() const
{
RefPtr page = contextDocument() ? contextDocument()->page() : nullptr;
if (!page)
return 0;
return page->storageNamespaceProvider().localStorageNamespace(page->sessionID()).storageAreaMapCountForTesting();
}
uint64_t Internals::elementIdentifier(Element& element) const
{
return element.nodeIdentifier().toUInt64();
}
bool Internals::isElementAlive(uint64_t elementIdentifier) const
{
return Node::fromIdentifier(ObjectIdentifier<NodeIdentifierType>(elementIdentifier));
}
uint64_t Internals::pageIdentifier(const Document& document) const
{
return document.pageID() ? document.pageID()->toUInt64() : 0;
}
bool Internals::isAnyWorkletGlobalScopeAlive() const
{
return WorkletGlobalScope::numberOfWorkletGlobalScopes();
}
String Internals::serviceWorkerClientInternalIdentifier(const Document& document) const
{
return document.identifier().toString();
}
RefPtr<WindowProxy> Internals::openDummyInspectorFrontend(const String& url)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return nullptr;
RefPtr inspectedPage = document->page();
RefPtr localMainFrame = dynamicDowncast<LocalFrame>(inspectedPage->mainFrame());
if (!localMainFrame)
return nullptr;
RefPtr window = localMainFrame->document()->window();
if (!window)
return nullptr;
#if PLATFORM(WPE)
WTF::registerInspectorResourceIfNeeded();
#endif
auto frontendWindowProxy = window->open(*window, *window, url, emptyAtom(), emptyString()).releaseReturnValue();
m_inspectorFrontend = makeUnique<InspectorStubFrontend>(*inspectedPage, *localMainFrame, downcast<LocalDOMWindow>(frontendWindowProxy->window()));
return frontendWindowProxy;
}
void Internals::closeDummyInspectorFrontend()
{
m_inspectorFrontend = nullptr;
}
ExceptionOr<void> Internals::setInspectorIsUnderTest(bool isUnderTest)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
document->protectedPage()->inspectorController().setIsUnderTest(isUnderTest);
return { };
}
unsigned Internals::numberOfScrollableAreas()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return 0;
unsigned count = 0;
LocalFrame* frame = document->frame();
if (frame->view()->scrollableAreas())
count += frame->view()->scrollableAreas()->computeSize();
for (RefPtr child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
RefPtr localChild = dynamicDowncast<LocalFrame>(child);
if (!localChild)
continue;
auto* frameView = localChild->view();
if (!frameView)
continue;
if (frameView->scrollableAreas())
count += frameView->scrollableAreas()->computeSize();
}
return count;
}
ExceptionOr<bool> Internals::isPageBoxVisible(int pageNumber)
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
return document->isPageBoxVisible(pageNumber);
}
static OptionSet<LayerTreeAsTextOptions> toLayerTreeAsTextOptions(unsigned short flags)
{
OptionSet<LayerTreeAsTextOptions> layerTreeFlags;
if (flags & Internals::LAYER_TREE_INCLUDES_VISIBLE_RECTS)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeVisibleRects);
if (flags & Internals::LAYER_TREE_INCLUDES_TILE_CACHES)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeTileCaches);
if (flags & Internals::LAYER_TREE_INCLUDES_REPAINT_RECTS)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeRepaintRects);
if (flags & Internals::LAYER_TREE_INCLUDES_PAINTING_PHASES)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludePaintingPhases);
if (flags & Internals::LAYER_TREE_INCLUDES_CONTENT_LAYERS)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeContentLayers);
if (flags & Internals::LAYER_TREE_INCLUDES_ACCELERATES_DRAWING)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeAcceleratesDrawing);
if (flags & Internals::LAYER_TREE_INCLUDES_CLIPPING)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeClipping);
if (flags & Internals::LAYER_TREE_INCLUDES_BACKING_STORE_ATTACHED)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeBackingStoreAttached);
if (flags & Internals::LAYER_TREE_INCLUDES_ROOT_LAYER_PROPERTIES)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeRootLayerProperties);
if (flags & Internals::LAYER_TREE_INCLUDES_EVENT_REGION)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeEventRegion);
if (flags & Internals::LAYER_TREE_INCLUDES_EXTENDED_COLOR)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeExtendedColor);
if (flags & Internals::LAYER_TREE_INCLUDES_DEVICE_SCALE)
layerTreeFlags.add(LayerTreeAsTextOptions::IncludeDeviceScale);
return layerTreeFlags;
}
// FIXME: Remove the document argument. It is almost always the same as
// contextDocument(), with the exception of a few tests that pass a
// different document, and could just make the call through another Internals
// instance instead.
ExceptionOr<String> Internals::layerTreeAsText(Document& document, unsigned short flags) const
{
if (!document.frame() || !document.frame()->contentRenderer())
return Exception { ExceptionCode::InvalidAccessError };
return document.frame()->contentRenderer()->compositor().layerTreeAsText(toLayerTreeAsTextOptions(flags));
}
ExceptionOr<uint64_t> Internals::layerIDForElement(Element& element)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
if (!element.renderer() || !element.renderer()->hasLayer())
return Exception { ExceptionCode::NotFoundError };
auto& layerModelObject = downcast<RenderLayerModelObject>(*element.renderer());
if (!layerModelObject.layer()->isComposited())
return Exception { ExceptionCode::NotFoundError };
auto* backing = layerModelObject.layer()->backing();
return backing->graphicsLayer()->primaryLayerID() ? backing->graphicsLayer()->primaryLayerID()->object().toUInt64() : 0;
}
static ExceptionOr<uint64_t> getLayerID(GraphicsLayer* layer)
{
if (!layer)
return Exception { ExceptionCode::NotFoundError };
auto primaryLayerID = layer->primaryLayerID();
if (!primaryLayerID)
return Exception { ExceptionCode::NotFoundError };
return primaryLayerID ? primaryLayerID->object().toUInt64() : 0;
}
ExceptionOr<uint64_t> Internals::horizontalScrollbarLayerID(Node* node) const
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
return getLayerID(areaOrException.returnValue()->layerForHorizontalScrollbar());
}
ExceptionOr<uint64_t> Internals::verticalScrollbarLayerID(Node* node) const
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
return getLayerID(areaOrException.returnValue()->layerForVerticalScrollbar());
}
ExceptionOr<Ref<DOMRect>> Internals::horizontalScrollbarFrameRect(Node* node) const
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
if (auto* scrollbar = areaOrException.returnValue()->horizontalScrollbar())
return DOMRect::create(scrollbar->frameRect());
return DOMRect::create();
}
ExceptionOr<Ref<DOMRect>> Internals::verticalScrollbarFrameRect(Node* node) const
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
if (auto* scrollbar = areaOrException.returnValue()->verticalScrollbar())
return DOMRect::create(scrollbar->frameRect());
return DOMRect::create();
}
ExceptionOr<Internals::ScrollingNodeID> Internals::scrollingNodeIDForNode(Node* node)
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
auto scrollingNodeID = areaOrException.returnValue()->scrollingNodeID();
if (!scrollingNodeID)
return { { 0, 0 } };
return { { scrollingNodeID->object().toUInt64(), scrollingNodeID->processIdentifier().toUInt64() } };
}
ExceptionOr<unsigned> Internals::scrollableAreaWidth(Node& node)
{
auto areaOrException = scrollableAreaForNode(&node);
if (areaOrException.hasException())
return areaOrException.releaseException();
auto* scrollableArea = areaOrException.releaseReturnValue();
return scrollableArea->contentsSize().width();
}
static OptionSet<PlatformLayerTreeAsTextFlags> toPlatformLayerTreeFlags(unsigned short flags)
{
OptionSet<PlatformLayerTreeAsTextFlags> platformLayerTreeFlags = { };
if (flags & Internals::PLATFORM_LAYER_TREE_DEBUG)
platformLayerTreeFlags.add(PlatformLayerTreeAsTextFlags::Debug);
if (flags & Internals::PLATFORM_LAYER_TREE_IGNORES_CHILDREN)
platformLayerTreeFlags.add(PlatformLayerTreeAsTextFlags::IgnoreChildren);
if (flags & Internals::PLATFORM_LAYER_TREE_INCLUDE_MODELS)
platformLayerTreeFlags.add(PlatformLayerTreeAsTextFlags::IncludeModels);
return platformLayerTreeFlags;
}
ExceptionOr<String> Internals::platformLayerTreeAsText(Element& element, unsigned short flags) const
{
Ref document = element.document();
if (!document->frame() || !document->frame()->contentRenderer())
return Exception { ExceptionCode::InvalidAccessError };
auto text = document->frame()->contentRenderer()->compositor().platformLayerTreeAsText(element, toPlatformLayerTreeFlags(flags));
if (!text)
return Exception { ExceptionCode::NotFoundError };
return String { text.value() };
}
ExceptionOr<String> Internals::repaintRectsAsText() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
return document->frame()->trackedRepaintRectsAsText();
}
ExceptionOr<ScrollableArea*> Internals::scrollableAreaForNode(Node* node) const
{
if (!node)
node = contextDocument();
if (!node)
return Exception { ExceptionCode::InvalidAccessError };
Ref nodeRef { *node };
nodeRef->document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
ScrollableArea* scrollableArea = nullptr;
if (is<Document>(nodeRef)) {
auto* frameView = downcast<Document>(nodeRef.get()).view();
if (!frameView)
return Exception { ExceptionCode::InvalidAccessError };
scrollableArea = frameView;
} else if (node == nodeRef->document().scrollingElement()) {
auto* frameView = nodeRef->document().view();
if (!frameView)
return Exception { ExceptionCode::InvalidAccessError };
scrollableArea = frameView;
} else if (RefPtr element = dynamicDowncast<Element>(nodeRef)) {
if (!element->renderBox())
return Exception { ExceptionCode::InvalidAccessError };
auto& renderBox = *element->renderBox();
if (auto* renderListBox = dynamicDowncast<RenderListBox>(renderBox))
scrollableArea = renderListBox;
else {
ASSERT(renderBox.layer());
scrollableArea = renderBox.layer()->scrollableArea();
}
} else
return Exception { ExceptionCode::InvalidNodeTypeError };
if (!scrollableArea)
return Exception { ExceptionCode::InvalidNodeTypeError };
return scrollableArea;
}
ExceptionOr<String> Internals::scrollbarOverlayStyle(Node* node) const
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
auto* scrollableArea = areaOrException.releaseReturnValue();
switch (scrollableArea->scrollbarOverlayStyle()) {
case ScrollbarOverlayStyle::Default:
return "default"_str;
case ScrollbarOverlayStyle::Dark:
return "dark"_str;
case ScrollbarOverlayStyle::Light:
return "light"_str;
}
ASSERT_NOT_REACHED();
return "unknown"_str;
}
ExceptionOr<bool> Internals::scrollbarUsingDarkAppearance(Node* node) const
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
auto* scrollableArea = areaOrException.releaseReturnValue();
return scrollableArea->useDarkAppearance();
}
ExceptionOr<String> Internals::horizontalScrollbarState(Node* node) const
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
auto* scrollableArea = areaOrException.releaseReturnValue();
return scrollableArea->horizontalScrollbarStateForTesting();
}
ExceptionOr<String> Internals::verticalScrollbarState(Node* node) const
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
auto* scrollableArea = areaOrException.releaseReturnValue();
return scrollableArea->verticalScrollbarStateForTesting();
}
static String scrollbarsControllerTypeString(ScrollbarsController& controller)
{
#if PLATFORM(MAC)
if (is<ScrollbarsControllerMac>(controller))
return "ScrollbarsControllerMac"_s;
#endif
if (is<ScrollbarsControllerMock>(controller))
return "ScrollbarsControllerMock"_s;
return "RemoteScrollbarsController"_s;
}
ExceptionOr<String> Internals::scrollbarsControllerTypeForNode(Node* node) const
{
auto areaOrException = scrollableAreaForNode(node);
if (areaOrException.hasException())
return areaOrException.releaseException();
auto* scrollableArea = areaOrException.releaseReturnValue();
return scrollbarsControllerTypeString(scrollableArea->scrollbarsController());
}
ExceptionOr<String> Internals::scrollingStateTreeAsText() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->updateLayout(LayoutOptions::IgnorePendingStylesheets);
RefPtr page = document->page();
if (!page)
return String();
return page->scrollingStateTreeAsText();
}
ExceptionOr<String> Internals::scrollingTreeAsText() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->updateLayout(LayoutOptions::IgnorePendingStylesheets);
RefPtr page = document->page();
if (!page)
return String();
RefPtr scrollingCoordinator = page->scrollingCoordinator();
if (!scrollingCoordinator)
return String();
scrollingCoordinator->commitTreeStateIfNeeded();
return scrollingCoordinator->scrollingTreeAsText();
}
ExceptionOr<bool> Internals::haveScrollingTree() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->updateLayout(LayoutOptions::IgnorePendingStylesheets);
RefPtr page = document->page();
if (!page)
return false;
RefPtr scrollingCoordinator = page->scrollingCoordinator();
if (!scrollingCoordinator)
return false;
return scrollingCoordinator->haveScrollingTree();
}
ExceptionOr<String> Internals::synchronousScrollingReasons() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
RefPtr page = document->page();
if (!page)
return String();
return page->synchronousScrollingReasonsAsText();
}
ExceptionOr<Ref<DOMRectList>> Internals::nonFastScrollableRects() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
RefPtr page = document->page();
if (!page)
return DOMRectList::create();
return page->nonFastScrollableRectsForTesting();
}
ExceptionOr<void> Internals::setElementUsesDisplayListDrawing(Element& element, bool usesDisplayListDrawing)
{
RefPtr document = contextDocument();
if (!document || !document->renderView())
return Exception { ExceptionCode::InvalidAccessError };
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
if (!element.renderer())
return Exception { ExceptionCode::InvalidAccessError };
if (!element.renderer()->hasLayer())
return Exception { ExceptionCode::InvalidAccessError };
CheckedPtr layer = downcast<RenderLayerModelObject>(element.renderer())->layer();
if (!layer->isComposited())
return Exception { ExceptionCode::InvalidAccessError };
layer->backing()->setUsesDisplayListDrawing(usesDisplayListDrawing);
return { };
}
ExceptionOr<void> Internals::setElementTracksDisplayListReplay(Element& element, bool isTrackingReplay)
{
RefPtr document = contextDocument();
if (!document || !document->renderView())
return Exception { ExceptionCode::InvalidAccessError };
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
if (!element.renderer())
return Exception { ExceptionCode::InvalidAccessError };
if (!element.renderer()->hasLayer())
return Exception { ExceptionCode::InvalidAccessError };
CheckedPtr layer = downcast<RenderLayerModelObject>(element.renderer())->layer();
if (!layer->isComposited())
return Exception { ExceptionCode::InvalidAccessError };
layer->backing()->setIsTrackingDisplayListReplay(isTrackingReplay);
return { };
}
static OptionSet<DisplayList::AsTextFlag> toDisplayListFlags(unsigned short flags)
{
OptionSet<DisplayList::AsTextFlag> displayListFlags;
if (flags & Internals::DISPLAY_LIST_INCLUDE_PLATFORM_OPERATIONS)
displayListFlags.add(DisplayList::AsTextFlag::IncludePlatformOperations);
if (flags & Internals::DISPLAY_LIST_INCLUDE_RESOURCE_IDENTIFIERS)
displayListFlags.add(DisplayList::AsTextFlag::IncludeResourceIdentifiers);
return displayListFlags;
}
ExceptionOr<String> Internals::displayListForElement(Element& element, unsigned short flags)
{
RefPtr document = contextDocument();
if (!document || !document->renderView())
return Exception { ExceptionCode::InvalidAccessError };
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
if (!element.renderer())
return Exception { ExceptionCode::InvalidAccessError };
if (!element.renderer()->hasLayer())
return Exception { ExceptionCode::InvalidAccessError };
CheckedPtr layer = downcast<RenderLayerModelObject>(element.renderer())->layer();
if (!layer->isComposited())
return Exception { ExceptionCode::InvalidAccessError };
return layer->backing()->displayListAsText(toDisplayListFlags(flags));
}
ExceptionOr<String> Internals::replayDisplayListForElement(Element& element, unsigned short flags)
{
RefPtr document = contextDocument();
if (!document || !document->renderView())
return Exception { ExceptionCode::InvalidAccessError };
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
if (!element.renderer())
return Exception { ExceptionCode::InvalidAccessError };
if (!element.renderer()->hasLayer())
return Exception { ExceptionCode::InvalidAccessError };
CheckedPtr layer = downcast<RenderLayerModelObject>(element.renderer())->layer();
if (!layer->isComposited())
return Exception { ExceptionCode::InvalidAccessError };
return layer->backing()->replayDisplayListAsText(toDisplayListFlags(flags));
}
void Internals::setForceUseGlyphDisplayListForTesting(bool enabled)
{
TextPainter::setForceUseGlyphDisplayListForTesting(enabled);
}
void Internals::clearGlyphDisplayListCacheForTesting()
{
TextPainter::clearGlyphDisplayListCacheForTesting();
}
ExceptionOr<String> Internals::cachedGlyphDisplayListsForTextNode(Node& node, unsigned short flags)
{
RefPtr document = contextDocument();
if (!document || !document->renderView())
return Exception { ExceptionCode::InvalidAccessError };
RefPtr textNode = dynamicDowncast<Text>(node);
if (!textNode)
return Exception { ExceptionCode::InvalidAccessError };
textNode->document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
if (!textNode->renderer())
return Exception { ExceptionCode::InvalidAccessError };
return TextPainter::cachedGlyphDisplayListsForTextNodeAsText(*textNode, toDisplayListFlags(flags));
}
ExceptionOr<void> Internals::garbageCollectDocumentResources() const
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
document->cachedResourceLoader().garbageCollectDocumentResources();
return { };
}
bool Internals::isUnderMemoryWarning()
{
return MemoryPressureHandler::singleton().isUnderMemoryWarning();
}
bool Internals::isUnderMemoryPressure()
{
return MemoryPressureHandler::singleton().isUnderMemoryPressure();
}
void Internals::beginSimulatedMemoryWarning()
{
MemoryPressureHandler::singleton().beginSimulatedMemoryWarning();
}
void Internals::endSimulatedMemoryWarning()
{
MemoryPressureHandler::singleton().endSimulatedMemoryWarning();
}
void Internals::beginSimulatedMemoryPressure()
{
MemoryPressureHandler::singleton().beginSimulatedMemoryPressure();
}
void Internals::endSimulatedMemoryPressure()
{
MemoryPressureHandler::singleton().endSimulatedMemoryPressure();
}
ExceptionOr<void> Internals::insertAuthorCSS(const String& css) const
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
auto parsedSheet = StyleSheetContents::create(*document);
parsedSheet.get().setIsUserStyleSheet(false);
parsedSheet.get().parseString(css);
document->extensionStyleSheets().addAuthorStyleSheetForTesting(WTF::move(parsedSheet));
return { };
}
ExceptionOr<void> Internals::insertUserCSS(const String& css) const
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
auto parsedSheet = StyleSheetContents::create(*document);
parsedSheet.get().setIsUserStyleSheet(true);
parsedSheet.get().parseString(css);
document->extensionStyleSheets().addUserStyleSheet(WTF::move(parsedSheet));
return { };
}
String Internals::counterValue(Element& element)
{
return counterValueForElement(&element);
}
int Internals::pageNumber(Element& element, float pageWidth, float pageHeight)
{
return PrintContext::pageNumberForElement(&element, { pageWidth, pageHeight });
}
Vector<String> Internals::shortcutIconURLs() const
{
if (!frame())
return { };
auto* documentLoader = frame()->loader().documentLoader();
if (!documentLoader)
return { };
Vector<String> result;
for (auto& linkIcon : documentLoader->linkIcons())
result.append(linkIcon.url.string());
return result;
}
int Internals::numberOfPages(float pageWidth, float pageHeight)
{
if (!frame())
return -1;
return PrintContext::numberOfPages(*frame(), FloatSize(pageWidth, pageHeight));
}
ExceptionOr<String> Internals::pageProperty(const String& propertyName, int pageNumber) const
{
if (!frame())
return Exception { ExceptionCode::InvalidAccessError };
return PrintContext::pageProperty(frame(), propertyName, pageNumber);
}
ExceptionOr<String> Internals::pageSizeAndMarginsInPixels(int pageNumber, int width, int height, int marginTop, int marginRight, int marginBottom, int marginLeft) const
{
if (!frame())
return Exception { ExceptionCode::InvalidAccessError };
return PrintContext::pageSizeAndMarginsInPixels(frame(), pageNumber, width, height, marginTop, marginRight, marginBottom, marginLeft);
}
ExceptionOr<float> Internals::pageScaleFactor() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
return document->page()->pageScaleFactor();
}
ExceptionOr<void> Internals::setPageZoomFactor(float zoomFactor)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->frame()->setPageZoomFactor(zoomFactor);
return { };
}
ExceptionOr<void> Internals::setTextZoomFactor(float zoomFactor)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->frame()->setTextZoomFactor(zoomFactor);
return { };
}
ExceptionOr<void> Internals::setUseFixedLayout(bool useFixedLayout)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->setUseFixedLayout(useFixedLayout);
return { };
}
ExceptionOr<void> Internals::setFixedLayoutSize(int width, int height)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->setFixedLayoutSize(IntSize(width, height));
return { };
}
ExceptionOr<void> Internals::setViewExposedRect(float x, float y, float width, float height)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->setViewExposedRect(FloatRect(x, y, width, height));
return { };
}
void Internals::setPrinting(int width, int height)
{
printContextForTesting() = PrintContext::create(frame());
printContextForTesting()->begin(width, height);
}
void Internals::setHeaderHeight(float height)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return;
document->page()->setHeaderHeight(height);
}
void Internals::setFooterHeight(float height)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return;
document->page()->setFooterHeight(height);
}
void Internals::setFullscreenInsets(FullscreenInsets insets)
{
Page* page = contextDocument()->frame()->page();
ASSERT(page);
page->setFullscreenInsets(FloatBoxExtent(insets.top, insets.right, insets.bottom, insets.left));
}
ExceptionOr<void> Internals::setFullscreenAutoHideDuration(double duration)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidStateError };
document->protectedPage()->setFullscreenAutoHideDuration(Seconds(duration));
return { };
}
void Internals::setScreenContentsFormatsForTesting(const Vector<Internals::ContentsFormat>& formats)
{
OptionSet<WebCore::ContentsFormat> contentsFormats;
for (auto format : formats) {
switch (format) {
case Internals::ContentsFormat::RGBA8:
contentsFormats.add(WebCore::ContentsFormat::RGBA8);
break;
#if ENABLE(PIXEL_FORMAT_RGB10)
case Internals::ContentsFormat::RGBA10:
contentsFormats.add(WebCore::ContentsFormat::RGBA10);
break;
#endif
#if ENABLE(PIXEL_FORMAT_RGBA16F)
case Internals::ContentsFormat::RGBA16F:
contentsFormats.add(WebCore::ContentsFormat::RGBA16F);
break;
#endif
}
}
#if HAVE(SUPPORT_HDR_DISPLAY)
WebCore::setScreenContentsFormatsForTesting(contentsFormats);
#else
UNUSED_PARAM(contentsFormats);
#endif
}
#if ENABLE(VIDEO)
bool Internals::isChangingPresentationMode(HTMLVideoElement& element) const
{
#if ENABLE(VIDEO_PRESENTATION_MODE)
return element.isChangingPresentationMode();
#else
UNUSED_PARAM(element);
return false;
#endif
}
#endif
#if ENABLE(VIDEO_PRESENTATION_MODE)
void Internals::setMockVideoPresentationModeEnabled(bool enabled)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return;
document->page()->chrome().client().setMockVideoPresentationModeEnabled(enabled);
}
#endif
void Internals::setCanvasNoiseInjectionSalt(HTMLCanvasElement& element, unsigned long long salt)
{
element.setNoiseInjectionSalt(salt);
}
bool Internals::doesCanvasHavePendingCanvasNoiseInjection(HTMLCanvasElement& element) const
{
return element.havePendingCanvasNoiseInjection();
}
void Internals::registerURLSchemeAsBypassingContentSecurityPolicy(const String& scheme)
{
LegacySchemeRegistry::registerURLSchemeAsBypassingContentSecurityPolicy(scheme);
}
void Internals::removeURLSchemeRegisteredAsBypassingContentSecurityPolicy(const String& scheme)
{
LegacySchemeRegistry::removeURLSchemeRegisteredAsBypassingContentSecurityPolicy(scheme);
}
void Internals::registerDefaultPortForProtocol(unsigned short port, const String& protocol)
{
registerDefaultPortForProtocolForTesting(port, protocol);
}
Ref<MallocStatistics> Internals::mallocStatistics() const
{
return MallocStatistics::create();
}
Ref<TypeConversions> Internals::typeConversions() const
{
return TypeConversions::create();
}
Ref<MemoryInfo> Internals::memoryInfo() const
{
return MemoryInfo::create();
}
Vector<String> Internals::getReferencedFilePaths() const
{
RefPtr localFrame = frame();
if (!localFrame)
return { };
localFrame->loader().history().saveDocumentAndScrollState();
return FormController::referencedFilePaths(localFrame->loader().history().currentItem()->documentState());
}
ExceptionOr<void> Internals::startTrackingRepaints()
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->setTracksRepaints(true);
return { };
}
ExceptionOr<void> Internals::stopTrackingRepaints()
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->setTracksRepaints(false);
return { };
}
ExceptionOr<void> Internals::startTrackingLayerFlushes()
{
RefPtr document = contextDocument();
if (!document || !document->renderView())
return Exception { ExceptionCode::InvalidAccessError };
document->renderView()->compositor().startTrackingLayerFlushes();
return { };
}
ExceptionOr<unsigned> Internals::layerFlushCount()
{
RefPtr document = contextDocument();
if (!document || !document->renderView())
return Exception { ExceptionCode::InvalidAccessError };
return document->renderView()->compositor().layerFlushCount();
}
ExceptionOr<void> Internals::startTrackingStyleRecalcs()
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
document->startTrackingStyleRecalcs();
return { };
}
ExceptionOr<unsigned> Internals::styleRecalcCount()
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
return document->styleRecalcCount();
}
unsigned Internals::lastStyleUpdateSize() const
{
RefPtr document = contextDocument();
if (!document)
return 0;
return document->lastStyleUpdateSizeForTesting();
}
ExceptionOr<void> Internals::startTrackingLayoutUpdates()
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->startTrackingLayoutUpdates();
return { };
}
ExceptionOr<unsigned> Internals::layoutUpdateCount()
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
return document->view()->layoutUpdateCount();
}
ExceptionOr<void> Internals::startTrackingRenderLayerPositionUpdates()
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
document->view()->startTrackingRenderLayerPositionUpdates();
return { };
}
ExceptionOr<unsigned> Internals::renderLayerPositionUpdateCount()
{
RefPtr document = contextDocument();
if (!document || !document->view())
return Exception { ExceptionCode::InvalidAccessError };
return document->view()->renderLayerPositionUpdateCount();
}
ExceptionOr<void> Internals::startTrackingCompositingUpdates()
{
RefPtr document = contextDocument();
if (!document || !document->renderView())
return Exception { ExceptionCode::InvalidAccessError };
document->renderView()->compositor().startTrackingCompositingUpdates();
return { };
}
ExceptionOr<unsigned> Internals::compositingUpdateCount()
{
RefPtr document = contextDocument();
if (!document || !document->renderView())
return Exception { ExceptionCode::InvalidAccessError };
return document->renderView()->compositor().compositingUpdateCount();
}
ExceptionOr<void> Internals::startTrackingRenderingUpdates()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
document->page()->startTrackingRenderingUpdates();
return { };
}
ExceptionOr<unsigned> Internals::renderingUpdateCount()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
return document->page()->renderingUpdateCount();
}
ExceptionOr<std::optional<double>> Internals::timeToNextRenderingUpdate()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
if (auto timeToNextUpdate = document->page()->timeToNextRenderingUpdateForTesting())
return timeToNextUpdate->milliseconds();
return { std::nullopt };
}
ExceptionOr<void> Internals::setCompositingPolicyOverride(std::optional<CompositingPolicy> policyOverride)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
if (!policyOverride) {
document->page()->setCompositingPolicyOverride(std::nullopt);
return { };
}
switch (policyOverride.value()) {
case Internals::CompositingPolicy::Normal:
document->page()->setCompositingPolicyOverride(WebCore::CompositingPolicy::Normal);
break;
case Internals::CompositingPolicy::Conservative:
document->page()->setCompositingPolicyOverride(WebCore::CompositingPolicy::Conservative);
break;
}
return { };
}
ExceptionOr<std::optional<Internals::CompositingPolicy>> Internals::compositingPolicyOverride() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
auto policyOverride = document->page()->compositingPolicyOverride();
if (!policyOverride)
return { std::nullopt };
switch (policyOverride.value()) {
case WebCore::CompositingPolicy::Normal:
return { Internals::CompositingPolicy::Normal };
case WebCore::CompositingPolicy::Conservative:
return { Internals::CompositingPolicy::Conservative };
}
return { Internals::CompositingPolicy::Normal };
}
void Internals::updateLayoutAndStyleForAllFrames() const
{
RefPtr document = contextDocument();
if (!document || !document->view())
return;
document->view()->updateLayoutAndStyleIfNeededRecursive();
}
ExceptionOr<void> Internals::updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks(Node* node)
{
Document* document;
if (!node)
document = contextDocument();
else if (auto* documentNode = dynamicDowncast<Document>(*node))
document = documentNode;
else if (auto* iframe = dynamicDowncast<HTMLIFrameElement>(*node))
document = iframe->contentDocument();
else
return Exception { ExceptionCode::TypeError };
document->updateLayout({ LayoutOptions::IgnorePendingStylesheets, LayoutOptions::RunPostLayoutTasksSynchronously });
document->flushDeferredAXObjectCacheUpdate();
return { };
}
#if !PLATFORM(IOS_FAMILY)
static ASCIILiteral cursorTypeToString(Cursor::Type cursorType)
{
switch (cursorType) {
case Cursor::Type::Pointer: return "Pointer"_s;
case Cursor::Type::Cross: return "Cross"_s;
case Cursor::Type::Hand: return "Hand"_s;
case Cursor::Type::IBeam: return "IBeam"_s;
case Cursor::Type::Wait: return "Wait"_s;
case Cursor::Type::Help: return "Help"_s;
case Cursor::Type::EastResize: return "EastResize"_s;
case Cursor::Type::NorthResize: return "NorthResize"_s;
case Cursor::Type::NorthEastResize: return "NorthEastResize"_s;
case Cursor::Type::NorthWestResize: return "NorthWestResize"_s;
case Cursor::Type::SouthResize: return "SouthResize"_s;
case Cursor::Type::SouthEastResize: return "SouthEastResize"_s;
case Cursor::Type::SouthWestResize: return "SouthWestResize"_s;
case Cursor::Type::WestResize: return "WestResize"_s;
case Cursor::Type::NorthSouthResize: return "NorthSouthResize"_s;
case Cursor::Type::EastWestResize: return "EastWestResize"_s;
case Cursor::Type::NorthEastSouthWestResize: return "NorthEastSouthWestResize"_s;
case Cursor::Type::NorthWestSouthEastResize: return "NorthWestSouthEastResize"_s;
case Cursor::Type::ColumnResize: return "ColumnResize"_s;
case Cursor::Type::RowResize: return "RowResize"_s;
case Cursor::Type::MiddlePanning: return "MiddlePanning"_s;
case Cursor::Type::EastPanning: return "EastPanning"_s;
case Cursor::Type::NorthPanning: return "NorthPanning"_s;
case Cursor::Type::NorthEastPanning: return "NorthEastPanning"_s;
case Cursor::Type::NorthWestPanning: return "NorthWestPanning"_s;
case Cursor::Type::SouthPanning: return "SouthPanning"_s;
case Cursor::Type::SouthEastPanning: return "SouthEastPanning"_s;
case Cursor::Type::SouthWestPanning: return "SouthWestPanning"_s;
case Cursor::Type::WestPanning: return "WestPanning"_s;
case Cursor::Type::Move: return "Move"_s;
case Cursor::Type::VerticalText: return "VerticalText"_s;
case Cursor::Type::Cell: return "Cell"_s;
case Cursor::Type::ContextMenu: return "ContextMenu"_s;
case Cursor::Type::Alias: return "Alias"_s;
case Cursor::Type::Progress: return "Progress"_s;
case Cursor::Type::NoDrop: return "NoDrop"_s;
case Cursor::Type::Copy: return "Copy"_s;
case Cursor::Type::None: return "None"_s;
case Cursor::Type::NotAllowed: return "NotAllowed"_s;
case Cursor::Type::ZoomIn: return "ZoomIn"_s;
case Cursor::Type::ZoomOut: return "ZoomOut"_s;
case Cursor::Type::Grab: return "Grab"_s;
case Cursor::Type::Grabbing: return "Grabbing"_s;
case Cursor::Type::Custom: return "Custom"_s;
case Cursor::Type::Invalid: break;
}
ASSERT_NOT_REACHED();
return "UNKNOWN"_s;
}
#endif
ExceptionOr<String> Internals::getCurrentCursorInfo()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
#if !PLATFORM(IOS_FAMILY)
Cursor cursor = document->frame()->eventHandler().currentMouseCursor();
StringBuilder result;
result.append("type="_s, cursorTypeToString(cursor.type()), " hotSpot="_s, cursor.hotSpot().x(), ',', cursor.hotSpot().y());
if (cursor.image()) {
FloatSize size = cursor.image()->size();
result.append(" image="_s, size.width(), 'x', size.height());
}
#if ENABLE(MOUSE_CURSOR_SCALE)
if (cursor.imageScaleFactor() != 1)
result.append(" scale="_s, cursor.imageScaleFactor());
#endif
return String { result.toString() };
#else
return "FAIL: Cursor details not available on this platform."_str;
#endif
}
Ref<ArrayBuffer> Internals::serializeObject(const RefPtr<SerializedScriptValue>& value) const
{
return ArrayBuffer::create(value->wireBytes());
}
Ref<SerializedScriptValue> Internals::deserializeBuffer(ArrayBuffer& buffer) const
{
return SerializedScriptValue::createFromWireBytes(buffer.toVector());
}
bool Internals::isFromCurrentWorld(JSC::JSValue value) const
{
JSC::VM& vm = contextDocument()->vm();
return isWorldCompatible(*vm.topCallFrame->lexicalGlobalObject(vm), value);
}
JSC::JSValue Internals::evaluateInWorldIgnoringException(const String& name, const String& source)
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return JSC::jsNull();
auto& scriptController = document->frame()->script();
auto world = ScriptController::createWorld(name);
return scriptController.executeScriptInWorldIgnoringException(world, source, JSC::SourceTaintedOrigin::Untainted);
}
#if !PLATFORM(MAC)
void Internals::setUsesOverlayScrollbars(bool enabled)
{
WebCore::DeprecatedGlobalSettings::setUsesOverlayScrollbars(enabled);
}
#endif
void Internals::forceAXObjectCacheUpdate() const
{
if (RefPtr document = contextDocument()) {
if (CheckedPtr cache = document->axObjectCache())
cache->performDeferredCacheUpdate(ForceLayout::Yes);
}
}
void Internals::forceReload(bool endToEnd)
{
OptionSet<ReloadOption> reloadOptions;
if (endToEnd)
reloadOptions.add(ReloadOption::FromOrigin);
if (RefPtr localFrame = frame())
localFrame->loader().reload(reloadOptions);
}
void Internals::reloadExpiredOnly()
{
if (RefPtr localFrame = frame())
localFrame->loader().reload(ReloadOption::ExpiredOnly);
}
void Internals::enableFixedWidthAutoSizeMode(bool enabled, int width, int height)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return;
document->view()->enableFixedWidthAutoSizeMode(enabled, { width, height });
}
void Internals::enableSizeToContentAutoSizeMode(bool enabled, int width, int height)
{
RefPtr document = contextDocument();
if (!document || !document->view())
return;
document->view()->enableSizeToContentAutoSizeMode(enabled, { width, height });
}
#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
void Internals::initializeMockCDM()
{
LegacyCDM::registerCDMFactory([] (LegacyCDM& cdm) { return makeUniqueWithoutRefCountedCheck<LegacyMockCDM>(cdm); },
LegacyMockCDM::supportsKeySystem, LegacyMockCDM::supportsKeySystemAndMimeType);
}
#endif
#if ENABLE(ENCRYPTED_MEDIA)
Ref<MockCDMFactory> Internals::registerMockCDM()
{
return MockCDMFactory::create();
}
#endif
String Internals::markerTextForListItem(Element& element)
{
return WebCore::markerTextForListItem(&element);
}
String Internals::toolTipFromElement(Element& element) const
{
HitTestResult result;
result.setInnerNode(&element);
TextDirection direction;
auto title = result.title(direction);
return !title.isEmpty() ? title : result.innerTextIfTruncated(direction);
}
String Internals::getImageSourceURL(Element& element)
{
return element.imageSourceURL();
}
#if ENABLE(VIDEO)
unsigned Internals::mediaElementCount()
{
RefPtr document = contextDocument();
if (!document)
return 0;
unsigned number = 0;
for (auto& mediaElement : HTMLMediaElement::allMediaElements()) {
if (&mediaElement->document() == document)
++number;
}
return number;
}
Vector<String> Internals::mediaResponseSources(HTMLMediaElement& media)
{
RefPtr resourceLoader = media.lastMediaResourceLoaderForTesting();
if (!resourceLoader)
return { };
Vector<String> result;
auto responses = resourceLoader->responsesForTesting();
for (auto& response : responses)
result.append(responseSourceToString(response));
return result;
}
Vector<String> Internals::mediaResponseContentRanges(HTMLMediaElement& media)
{
RefPtr resourceLoader = media.lastMediaResourceLoaderForTesting();
if (!resourceLoader)
return { };
Vector<String> result;
auto responses = resourceLoader->responsesForTesting();
for (auto& response : responses)
result.append(response.httpHeaderField(HTTPHeaderName::ContentRange));
return result;
}
void Internals::simulateAudioInterruption(HTMLMediaElement& element)
{
#if USE(GSTREAMER)
element.protectedPlayer()->simulateAudioInterruption();
#else
UNUSED_PARAM(element);
#endif
}
ExceptionOr<bool> Internals::mediaElementHasCharacteristic(HTMLMediaElement& element, const String& characteristic)
{
if (equalLettersIgnoringASCIICase(characteristic, "audible"_s))
return element.hasAudio();
if (equalLettersIgnoringASCIICase(characteristic, "visual"_s))
return element.hasVideo();
if (equalLettersIgnoringASCIICase(characteristic, "legible"_s))
return element.hasClosedCaptions();
return Exception { ExceptionCode::SyntaxError };
}
void Internals::enterViewerMode(HTMLVideoElement& element)
{
element.enterFullscreen(HTMLMediaElementEnums::VideoFullscreenModeInWindow);
}
ExceptionOr<bool> Internals::mediaPlayerRenderingCanBeAccelerated(HTMLMediaElement& element)
{
return element.mediaPlayerRenderingCanBeAccelerated();
}
bool Internals::elementShouldBufferData(HTMLMediaElement& element)
{
return element.bufferingPolicy() < MediaPlayer::BufferingPolicy::LimitReadAhead;
}
String Internals::elementBufferingPolicy(HTMLMediaElement& element)
{
switch (element.bufferingPolicy()) {
case MediaPlayer::BufferingPolicy::Default:
return "Default"_s;
case MediaPlayer::BufferingPolicy::LimitReadAhead:
return "LimitReadAhead"_s;
case MediaPlayer::BufferingPolicy::MakeResourcesPurgeable:
return "MakeResourcesPurgeable"_s;
case MediaPlayer::BufferingPolicy::PurgeResources:
return "PurgeResources"_s;
}
ASSERT_NOT_REACHED();
return "UNKNOWN"_s;
}
void Internals::setMediaElementBufferingPolicy(HTMLMediaElement& element, const String& policy)
{
if (policy == "Default"_s) {
element.setBufferingPolicy(MediaPlayer::BufferingPolicy::Default);
return;
}
if (policy == "LimitReadAhead"_s) {
element.setBufferingPolicy(MediaPlayer::BufferingPolicy::LimitReadAhead);
return;
}
if (policy == "MakeResourcesPurgeable"_s) {
element.setBufferingPolicy(MediaPlayer::BufferingPolicy::MakeResourcesPurgeable);
return;
}
if (policy == "PurgeResources"_s) {
element.setBufferingPolicy(MediaPlayer::BufferingPolicy::PurgeResources);
return;
}
ASSERT_NOT_REACHED();
}
ExceptionOr<void> Internals::setOverridePreferredDynamicRangeMode(HTMLMediaElement& element, const String& modeString)
{
DynamicRangeMode mode;
if (modeString == "None"_s)
mode = DynamicRangeMode::None;
else if (modeString == "Standard"_s)
mode = DynamicRangeMode::Standard;
else if (modeString == "HLG"_s)
mode = DynamicRangeMode::HLG;
else if (modeString == "HDR10"_s)
mode = DynamicRangeMode::HDR10;
else if (modeString == "DolbyVisionPQ"_s)
mode = DynamicRangeMode::DolbyVisionPQ;
else
return Exception { ExceptionCode::SyntaxError };
element.setOverridePreferredDynamicRangeMode(mode);
return { };
}
void Internals::enableGStreamerHolePunching(HTMLVideoElement& element)
{
#if USE(GSTREAMER)
element.enableGStreamerHolePunching();
#else
UNUSED_PARAM(element);
#endif
}
double Internals::effectiveDynamicRangeLimitValue(const HTMLMediaElement& media)
{
return media.computePlayerDynamicRangeLimit().value();
}
#endif
ExceptionOr<double> Internals::getContextEffectiveDynamicRangeLimitValue(const HTMLCanvasElement& canvas)
{
auto value = canvas.getContextEffectiveDynamicRangeLimitValue();
if (value.has_value())
return *value;
return Exception { ExceptionCode::InvalidStateError };
}
ExceptionOr<void> Internals::setPageShouldSuppressHDR(bool shouldSuppressHDR)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
document->page()->setShouldSuppressHDR(shouldSuppressHDR);
return { };
}
bool Internals::isSelectPopupVisible(HTMLSelectElement& element)
{
element.document().updateLayout(LayoutOptions::IgnorePendingStylesheets);
#if !PLATFORM(IOS_FAMILY)
return element.popupIsVisible();
#else
return false;
#endif
}
ExceptionOr<String> Internals::captionsStyleSheetOverride()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
#if ENABLE(VIDEO)
return document->page()->group().ensureCaptionPreferences().captionsStyleSheetOverride();
#else
return String { emptyString() };
#endif
}
ExceptionOr<void> Internals::setCaptionsStyleSheetOverride(const String& override)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
#if ENABLE(VIDEO)
document->page()->group().ensureCaptionPreferences().setCaptionsStyleSheetOverride(override);
#else
UNUSED_PARAM(override);
#endif
return { };
}
ExceptionOr<void> Internals::setPrimaryAudioTrackLanguageOverride(const String& language)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
#if ENABLE(VIDEO)
document->page()->group().ensureCaptionPreferences().setPrimaryAudioTrackLanguageOverride(language);
#else
UNUSED_PARAM(language);
#endif
return { };
}
ExceptionOr<void> Internals::setPreferredAudioCharacteristicsForTesting(const Vector<String>& characteristics)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
#if ENABLE(VIDEO)
document->page()->group().ensureCaptionPreferences().setPreferredAudioCharacteristicsForTesting(characteristics);
#else
UNUSED_PARAM(characteristics);
#endif
return { };
}
ExceptionOr<void> Internals::setCaptionDisplayMode(const String& mode)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
#if ENABLE(VIDEO)
Ref captionPreferences = document->page()->group().ensureCaptionPreferences();
if (equalLettersIgnoringASCIICase(mode, "automatic"_s))
captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode::Automatic);
else if (equalLettersIgnoringASCIICase(mode, "forcedonly"_s))
captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode::ForcedOnly);
else if (equalLettersIgnoringASCIICase(mode, "alwayson"_s))
captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode::AlwaysOn);
else if (equalLettersIgnoringASCIICase(mode, "manual"_s))
captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode::Manual);
else
return Exception { ExceptionCode::SyntaxError };
#else
UNUSED_PARAM(mode);
#endif
return { };
}
String Internals::captionDisplayMode() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return emptyString();
#if ENABLE(VIDEO)
switch (document->page()->group().ensureCaptionPreferences().captionDisplayMode()) {
case CaptionUserPreferences::CaptionDisplayMode::Automatic:
return "automatic"_s;
case CaptionUserPreferences::CaptionDisplayMode::ForcedOnly:
return "forcedonly"_s;
case CaptionUserPreferences::CaptionDisplayMode::AlwaysOn:
return "alwayson"_s;
case CaptionUserPreferences::CaptionDisplayMode::Manual:
return "manual"_s;
}
#endif
return emptyString();
}
#if ENABLE(VIDEO)
RefPtr<TextTrackCueGeneric> Internals::createGenericCue(double startTime, double endTime, String text)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return nullptr;
return TextTrackCueGeneric::create(*document, MediaTime::createWithDouble(startTime), MediaTime::createWithDouble(endTime), text);
}
ExceptionOr<String> Internals::textTrackBCP47Language(TextTrack& track)
{
return String { track.validBCP47Language() };
}
Ref<TimeRanges> Internals::createTimeRanges(Float32Array& startTimes, Float32Array& endTimes)
{
ASSERT(startTimes.length() == endTimes.length());
Ref<TimeRanges> ranges = TimeRanges::create();
unsigned count = std::min(startTimes.length(), endTimes.length());
for (unsigned i = 0; i < count; ++i)
ranges->add(startTimes.item(i), endTimes.item(i));
return ranges;
}
double Internals::closestTimeToTimeRanges(double time, TimeRanges& ranges)
{
return ranges.nearest(time);
}
void Internals::showCaptionDisplaySettingsPreviewForMediaElement(HTMLMediaElement& element)
{
element.showCaptionDisplaySettingsPreview();
}
void Internals::hideCaptionDisplaySettingsPreviewForMediaElement(HTMLMediaElement& element)
{
element.hideCaptionDisplaySettingsPreview();
}
void Internals::setMockCaptionDisplaySettingsClientCallback(RefPtr<MockCaptionDisplaySettingsClientCallback>&& callback)
{
if (m_mockCaptionDisplaySettingsClientCallback == callback)
return;
m_mockCaptionDisplaySettingsClientCallback = WTF::move(callback);
RefPtr frame = this->frame();
if (!frame)
return;
RefPtr page = frame->page();
if (!page)
return;
page->clearCaptionDisplaySettingsClientForTesting();
if (m_mockCaptionDisplaySettingsClientCallback)
page->setCaptionDisplaySettingsClientForTesting(*m_mockCaptionDisplaySettingsClientCallback);
}
MockCaptionDisplaySettingsClientCallback* Internals::mockCaptionDisplaySettingsClientCallback() const
{
return m_mockCaptionDisplaySettingsClientCallback.get();
}
RefPtr<MediaControlsHost> Internals::controlsHostForMediaElement(HTMLMediaElement& mediaElement)
{
return mediaElement.mediaControlsHost();
}
#endif
ExceptionOr<Ref<DOMRect>> Internals::selectionBounds()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
return DOMRect::create(document->frame()->selection().selectionBounds());
}
ExceptionOr<RefPtr<StaticRange>> Internals::selectedRange()
{
RefPtr document = contextDocument();
if (!document)
return Exception { ExceptionCode::InvalidAccessError };
auto range = contextDocument()->selection().selection().range();
if (!range)
return nullptr;
return RefPtr { StaticRange::create(*range) };
}
void Internals::setSelectionWithoutValidation(Ref<Node> baseNode, unsigned baseOffset, RefPtr<Node> extentNode, unsigned extentOffset)
{
contextDocument()->frame()->selection().moveTo(
VisiblePosition { makeDeprecatedLegacyPosition(baseNode.ptr(), baseOffset) },
VisiblePosition { makeDeprecatedLegacyPosition(extentNode.get(), extentOffset) });
}
void Internals::setSelectionFromNone()
{
if (RefPtr localFrame = frame())
localFrame->selection().setSelectionFromNone();
}
#if ENABLE(MEDIA_SOURCE)
void Internals::initializeMockMediaSource()
{
RefPtr document = contextDocument();
if (!document || (!document->settings().mediaSourceEnabled() && !document->settings().managedMediaSourceEnabled()))
return;
platformStrategies()->mediaStrategy()->enableMockMediaSource();
}
void Internals::setMaximumSourceBufferSize(SourceBuffer& buffer, uint64_t maximumSize, DOMPromiseDeferred<void>&& promise)
{
buffer.setMaximumSourceBufferSize(maximumSize)->whenSettled(RunLoop::currentSingleton(), [promise = WTF::move(promise)]() mutable {
promise.resolve();
});
}
void Internals::bufferedSamplesForTrackId(SourceBuffer& buffer, const AtomString& trackId, BufferedSamplesPromise&& promise)
{
buffer.bufferedSamplesForTrackId(parseInteger<uint64_t>(trackId).value_or(0))->whenSettled(RunLoop::currentSingleton(), [promise = WTF::move(promise)](auto&& samples) mutable {
if (!samples) {
promise.reject(Exception { ExceptionCode::OperationError, makeString("Error "_s, samples.error()) });
return;
}
promise.resolve(WTF::move(*samples));
});
}
void Internals::enqueuedSamplesForTrackID(SourceBuffer& buffer, const AtomString& trackID, BufferedSamplesPromise&& promise)
{
buffer.enqueuedSamplesForTrackID(parseInteger<uint64_t>(trackID).value_or(0))->whenSettled(RunLoop::currentSingleton(), [promise = WTF::move(promise)](auto&& samples) mutable {
if (!samples) {
promise.reject(Exception { ExceptionCode::OperationError, makeString("Error "_s, samples.error()) });
return;
}
promise.resolve(WTF::move(*samples));
});
}
double Internals::minimumUpcomingPresentationTimeForTrackID(SourceBuffer& buffer, const AtomString& trackID)
{
return buffer.minimumUpcomingPresentationTimeForTrackID(parseInteger<TrackID>(trackID).value_or(0)).toDouble();
}
void Internals::setShouldGenerateTimestamps(SourceBuffer& buffer, bool flag)
{
buffer.setShouldGenerateTimestamps(flag);
}
void Internals::setMaximumQueueDepthForTrackID(SourceBuffer& buffer, const AtomString& trackID, size_t maxQueueDepth)
{
buffer.setMaximumQueueDepthForTrackID(parseInteger<TrackID>(trackID).value_or(0), maxQueueDepth);
}
size_t Internals::evictableSize(SourceBuffer& buffer)
{
return buffer.evictableSize();
}
#endif
void Internals::enableMockMediaCapabilities()
{
PlatformMediaEngineConfigurationFactory::enableMock();
}
#if ENABLE(VIDEO)
ExceptionOr<void> Internals::beginMediaSessionInterruption(const String& interruptionString)
{
RefPtr manager = sessionManager();
if (!manager)
return Exception { ExceptionCode::InvalidAccessError };
PlatformMediaSession::InterruptionType interruption = PlatformMediaSession::InterruptionType::SystemInterruption;
if (equalLettersIgnoringASCIICase(interruptionString, "system"_s))
interruption = PlatformMediaSession::InterruptionType::SystemInterruption;
else if (equalLettersIgnoringASCIICase(interruptionString, "systemsleep"_s))
interruption = PlatformMediaSession::InterruptionType::SystemSleep;
else if (equalLettersIgnoringASCIICase(interruptionString, "enteringbackground"_s))
interruption = PlatformMediaSession::InterruptionType::EnteringBackground;
else if (equalLettersIgnoringASCIICase(interruptionString, "suspendedunderlock"_s))
interruption = PlatformMediaSession::InterruptionType::SuspendedUnderLock;
else
return Exception { ExceptionCode::InvalidAccessError };
manager->beginInterruption(interruption);
return { };
}
void Internals::endMediaSessionInterruption(const String& flagsString)
{
PlatformMediaSession::EndInterruptionFlags flags = PlatformMediaSession::EndInterruptionFlags::NoFlags;
if (equalLettersIgnoringASCIICase(flagsString, "mayresumeplaying"_s))
flags = PlatformMediaSession::EndInterruptionFlags::MayResumePlaying;
if (RefPtr manager = sessionManager())
manager->endInterruption(flags);
}
void Internals::applicationWillBecomeInactive()
{
if (RefPtr manager = sessionManager())
manager->applicationWillBecomeInactive();
}
void Internals::applicationDidBecomeActive()
{
if (RefPtr manager = sessionManager())
manager->applicationDidBecomeActive();
}
void Internals::applicationWillEnterForeground(bool suspendedUnderLock) const
{
if (RefPtr manager = sessionManager())
manager->applicationWillEnterForeground(suspendedUnderLock);
}
void Internals::applicationDidEnterBackground(bool suspendedUnderLock) const
{
if (RefPtr manager = sessionManager())
manager->applicationDidEnterBackground(suspendedUnderLock);
}
static PlatformMediaSession::MediaType mediaTypeFromString(const String& mediaTypeString)
{
if (equalLettersIgnoringASCIICase(mediaTypeString, "video"_s))
return PlatformMediaSession::MediaType::Video;
if (equalLettersIgnoringASCIICase(mediaTypeString, "audio"_s))
return PlatformMediaSession::MediaType::Audio;
if (equalLettersIgnoringASCIICase(mediaTypeString, "videoaudio"_s))
return PlatformMediaSession::MediaType::VideoAudio;
if (equalLettersIgnoringASCIICase(mediaTypeString, "webaudio"_s))
return PlatformMediaSession::MediaType::WebAudio;
return PlatformMediaSession::MediaType::None;
}
ExceptionOr<void> Internals::setMediaSessionRestrictions(const String& mediaTypeString, StringView restrictionsString)
{
auto mediaType = mediaTypeFromString(mediaTypeString);
if (mediaType == PlatformMediaSession::MediaType::None)
return Exception { ExceptionCode::InvalidAccessError };
RefPtr manager = sessionManager();
if (!manager)
return Exception { ExceptionCode::InvalidAccessError };
auto restrictions = manager->restrictions(mediaType);
manager->removeRestriction(mediaType, restrictions);
restrictions = MediaSessionRestriction::NoRestrictions;
for (StringView restrictionString : restrictionsString.split(',')) {
if (equalLettersIgnoringASCIICase(restrictionString, "concurrentplaybacknotpermitted"_s))
restrictions |= MediaSessionRestriction::ConcurrentPlaybackNotPermitted;
if (equalLettersIgnoringASCIICase(restrictionString, "backgroundprocessplaybackrestricted"_s))
restrictions |= MediaSessionRestriction::BackgroundProcessPlaybackRestricted;
if (equalLettersIgnoringASCIICase(restrictionString, "backgroundtabplaybackrestricted"_s))
restrictions |= MediaSessionRestriction::BackgroundTabPlaybackRestricted;
if (equalLettersIgnoringASCIICase(restrictionString, "interruptedplaybacknotpermitted"_s))
restrictions |= MediaSessionRestriction::InterruptedPlaybackNotPermitted;
if (equalLettersIgnoringASCIICase(restrictionString, "inactiveprocessplaybackrestricted"_s))
restrictions |= MediaSessionRestriction::InactiveProcessPlaybackRestricted;
if (equalLettersIgnoringASCIICase(restrictionString, "suspendedunderlockplaybackrestricted"_s))
restrictions |= MediaSessionRestriction::SuspendedUnderLockPlaybackRestricted;
}
manager->addRestriction(mediaType, restrictions);
return { };
}
ExceptionOr<String> Internals::mediaSessionRestrictions(const String& mediaTypeString) const
{
PlatformMediaSession::MediaType mediaType = mediaTypeFromString(mediaTypeString);
if (mediaType == PlatformMediaSession::MediaType::None)
return Exception { ExceptionCode::InvalidAccessError };
RefPtr manager = sessionManager();
if (!manager)
return Exception { ExceptionCode::InvalidAccessError };
auto restrictions = manager->restrictions(mediaType);
if (restrictions == MediaSessionRestriction::NoRestrictions)
return String();
StringBuilder builder;
if (restrictions & MediaSessionRestriction::ConcurrentPlaybackNotPermitted)
builder.append("concurrentplaybacknotpermitted"_s);
if (restrictions & MediaSessionRestriction::BackgroundProcessPlaybackRestricted) {
if (!builder.isEmpty())
builder.append(',');
builder.append("backgroundprocessplaybackrestricted"_s);
}
if (restrictions & MediaSessionRestriction::BackgroundTabPlaybackRestricted) {
if (!builder.isEmpty())
builder.append(',');
builder.append("backgroundtabplaybackrestricted"_s);
}
if (restrictions & MediaSessionRestriction::InterruptedPlaybackNotPermitted) {
if (!builder.isEmpty())
builder.append(',');
builder.append("interruptedplaybacknotpermitted"_s);
}
return String { builder.toString() };
}
void Internals::setMediaElementRestrictions(HTMLMediaElement& element, StringView restrictionsString)
{
MediaElementSession::BehaviorRestrictions restrictions = element.mediaSession().behaviorRestrictions();
element.mediaSession().removeBehaviorRestriction(restrictions);
restrictions = MediaElementSession::NoRestrictions;
for (StringView restrictionString : restrictionsString.split(',')) {
if (equalLettersIgnoringASCIICase(restrictionString, "norestrictions"_s))
restrictions |= MediaElementSession::NoRestrictions;
if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforload"_s))
restrictions |= MediaElementSession::RequireUserGestureForLoad;
if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforvideoratechange"_s))
restrictions |= MediaElementSession::RequireUserGestureForVideoRateChange;
if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforfullscreen"_s))
restrictions |= MediaElementSession::RequireUserGestureForFullscreen;
if (equalLettersIgnoringASCIICase(restrictionString, "requirepageconsenttoloadmedia"_s))
restrictions |= MediaElementSession::RequirePageConsentToLoadMedia;
if (equalLettersIgnoringASCIICase(restrictionString, "requirepageconsenttoresumemedia"_s))
restrictions |= MediaElementSession::RequirePageConsentToResumeMedia;
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
if (equalLettersIgnoringASCIICase(restrictionString, "requireusergesturetoshowplaybacktargetpicker"_s))
restrictions |= MediaElementSession::RequireUserGestureToShowPlaybackTargetPicker;
if (equalLettersIgnoringASCIICase(restrictionString, "wirelessvideoplaybackdisabled"_s))
restrictions |= MediaElementSession::WirelessVideoPlaybackDisabled;
#endif
if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforaudioratechange"_s))
restrictions |= MediaElementSession::RequireUserGestureForAudioRateChange;
if (equalLettersIgnoringASCIICase(restrictionString, "autopreloadingnotpermitted"_s))
restrictions |= MediaElementSession::AutoPreloadingNotPermitted;
if (equalLettersIgnoringASCIICase(restrictionString, "invisibleautoplaynotpermitted"_s))
restrictions |= MediaElementSession::InvisibleAutoplayNotPermitted;
if (equalLettersIgnoringASCIICase(restrictionString, "overrideusergesturerequirementformaincontent"_s))
restrictions |= MediaElementSession::OverrideUserGestureRequirementForMainContent;
if (equalLettersIgnoringASCIICase(restrictionString, "requireusergesturetocontrolcontrolsmanager"_s))
restrictions |= MediaElementSession::RequireUserGestureToControlControlsManager;
if (equalLettersIgnoringASCIICase(restrictionString, "requireplaybackTocontrolcontrolsmanager"_s))
restrictions |= MediaElementSession::RequirePlaybackToControlControlsManager;
if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforvideoduetolowpowermode"_s))
restrictions |= MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode;
if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforvideoduetoaggressivethermalmitigation"_s))
restrictions |= MediaElementSession::RequireUserGestureForVideoDueToAggressiveThermalMitigation;
if (equalLettersIgnoringASCIICase(restrictionString, "requirepagevisibilitytoplayaudio"_s))
restrictions |= MediaElementSession::RequirePageVisibilityToPlayAudio;
#if ENABLE(REQUIRES_PAGE_VISIBILITY_FOR_NOW_PLAYING)
if (equalLettersIgnoringASCIICase(restrictionString, "requirepagevisibilityforvideotobenowplaying"_s))
restrictions |= MediaElementSession::RequirePageVisibilityForVideoToBeNowPlaying;
#endif
}
element.mediaSession().addBehaviorRestriction(restrictions);
}
ExceptionOr<void> Internals::postRemoteControlCommand(const String& commandString, float argument)
{
RefPtr manager = sessionManager();
if (!manager)
return Exception { ExceptionCode::InvalidAccessError };
PlatformMediaSession::RemoteControlCommandType command;
PlatformMediaSession::RemoteCommandArgument parameter { argument, { } };
if (equalLettersIgnoringASCIICase(commandString, "play"_s))
command = PlatformMediaSession::RemoteControlCommandType::PlayCommand;
else if (equalLettersIgnoringASCIICase(commandString, "pause"_s))
command = PlatformMediaSession::RemoteControlCommandType::PauseCommand;
else if (equalLettersIgnoringASCIICase(commandString, "stop"_s))
command = PlatformMediaSession::RemoteControlCommandType::StopCommand;
else if (equalLettersIgnoringASCIICase(commandString, "toggleplaypause"_s))
command = PlatformMediaSession::RemoteControlCommandType::TogglePlayPauseCommand;
else if (equalLettersIgnoringASCIICase(commandString, "beginseekingbackward"_s))
command = PlatformMediaSession::RemoteControlCommandType::BeginSeekingBackwardCommand;
else if (equalLettersIgnoringASCIICase(commandString, "endseekingbackward"_s))
command = PlatformMediaSession::RemoteControlCommandType::EndSeekingBackwardCommand;
else if (equalLettersIgnoringASCIICase(commandString, "beginseekingforward"_s))
command = PlatformMediaSession::RemoteControlCommandType::BeginSeekingForwardCommand;
else if (equalLettersIgnoringASCIICase(commandString, "endseekingforward"_s))
command = PlatformMediaSession::RemoteControlCommandType::EndSeekingForwardCommand;
else if (equalLettersIgnoringASCIICase(commandString, "seektoplaybackposition"_s))
command = PlatformMediaSession::RemoteControlCommandType::SeekToPlaybackPositionCommand;
else if (equalLettersIgnoringASCIICase(commandString, "beginscrubbing"_s))
command = PlatformMediaSession::RemoteControlCommandType::BeginScrubbingCommand;
else if (equalLettersIgnoringASCIICase(commandString, "endscrubbing"_s))
command = PlatformMediaSession::RemoteControlCommandType::EndScrubbingCommand;
else
return Exception { ExceptionCode::InvalidAccessError };
manager->processDidReceiveRemoteControlCommand(command, parameter);
return { };
}
void Internals::activeAudioRouteDidChange(bool shouldPause)
{
#if PLATFORM(IOS) || PLATFORM(VISION)
MediaSessionHelper::sharedHelper().activeAudioRouteDidChange(shouldPause ? MediaSessionHelperClient::ShouldPause::Yes : MediaSessionHelperClient::ShouldPause::No);
#else
UNUSED_PARAM(shouldPause);
#endif
}
bool Internals::elementIsBlockingDisplaySleep(const HTMLMediaElement& element) const
{
return element.isDisablingSleep();
}
bool Internals::isPlayerVisibleInViewport(const HTMLMediaElement& element) const
{
RefPtr player = element.player();
return player && player->isVisibleInViewport();
}
bool Internals::isPlayerMuted(const HTMLMediaElement& element) const
{
RefPtr player = element.player();
return player && player->muted();
}
bool Internals::isPlayerPaused(const HTMLMediaElement& element) const
{
RefPtr player = element.player();
return player && player->paused();
}
void Internals::forceStereoDecoding(HTMLMediaElement& element)
{
element.forceStereoDecoding();
}
void Internals::beginAudioSessionInterruption()
{
#if USE(AUDIO_SESSION)
AudioSession::singleton().beginInterruptionForTesting();
#endif
}
void Internals::endAudioSessionInterruption()
{
#if USE(AUDIO_SESSION)
AudioSession::singleton().endInterruptionForTesting();
#endif
}
void Internals::clearAudioSessionInterruptionFlag()
{
#if USE(AUDIO_SESSION)
AudioSession::singleton().clearInterruptionFlagForTesting();
#endif
}
void Internals::suspendAllMediaBuffering()
{
RefPtr frame = this->frame();
if (!frame)
return;
RefPtr page = frame->page();
if (!page)
return;
page->suspendAllMediaBuffering();
}
void Internals::suspendAllMediaPlayback()
{
RefPtr frame = this->frame();
if (!frame)
return;
RefPtr page = frame->page();
if (!page)
return;
page->suspendAllMediaPlayback();
}
void Internals::resumeAllMediaPlayback()
{
RefPtr frame = this->frame();
if (!frame)
return;
RefPtr page = frame->page();
if (!page)
return;
page->resumeAllMediaPlayback();
}
#endif // ENABLE(VIDEO)
#if ENABLE(WEB_AUDIO)
void Internals::setAudioContextRestrictions(AudioContext& context, StringView restrictionsString)
{
auto restrictions = context.behaviorRestrictions();
context.removeBehaviorRestriction(restrictions);
restrictions = { };
for (StringView restrictionString : restrictionsString.split(',')) {
if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforaudiostart"_s))
restrictions.add(AudioContext::BehaviorRestrictionFlags::RequireUserGestureForAudioStartRestriction);
if (equalLettersIgnoringASCIICase(restrictionString, "requirepageconsentforaudiostart"_s))
restrictions.add(AudioContext::BehaviorRestrictionFlags::RequirePageConsentForAudioStartRestriction);
}
context.addBehaviorRestriction(restrictions);
}
Vector<float> Internals::waveShaperProcessCurveWithData(Vector<float> source, Vector<float> curve)
{
Vector<float> destination(source.size(), 0.0f);
WaveShaperDSPKernel::processCurveWithData(std::span { source }, std::span { destination }, std::span { curve });
return destination;
}
void Internals::useMockAudioDestinationCocoa()
{
#if PLATFORM(COCOA)
AudioDestinationCocoa::createOverride = MockAudioDestinationCocoa::create;
#endif
}
#endif
void Internals::simulateSystemSleep() const
{
#if ENABLE(VIDEO)
if (RefPtr manager = sessionManager())
manager->processSystemWillSleep();
#endif
}
void Internals::simulateSystemWake() const
{
#if ENABLE(VIDEO)
if (RefPtr manager = sessionManager())
manager->processSystemDidWake();
#endif
}
std::optional<Internals::NowPlayingMetadata> Internals::nowPlayingMetadata() const
{
RefPtr manager = sessionManager();
if (!manager)
return std::nullopt;
if (auto nowPlayingInfo = manager->nowPlayingInfo()) {
return { {
nowPlayingInfo->metadata.title,
nowPlayingInfo->metadata.artist,
nowPlayingInfo->metadata.album,
nowPlayingInfo->metadata.sourceApplicationIdentifier,
nowPlayingInfo->metadata.artwork ? std::optional { NowPlayingInfoArtwork {
nowPlayingInfo->metadata.artwork->src,
nowPlayingInfo->metadata.artwork->mimeType
} } : std::nullopt,
} };
}
return std::nullopt;
}
ExceptionOr<Internals::NowPlayingState> Internals::nowPlayingState() const
{
#if ENABLE(VIDEO)
RefPtr manager = sessionManager();
if (!manager)
return Exception { ExceptionCode::InvalidAccessError };
auto lastUpdatedNowPlayingInfoUniqueIdentifier = manager->lastUpdatedNowPlayingInfoUniqueIdentifier();
return { { manager->lastUpdatedNowPlayingTitle(),
manager->lastUpdatedNowPlayingDuration(),
manager->lastUpdatedNowPlayingElapsedTime(),
lastUpdatedNowPlayingInfoUniqueIdentifier ? lastUpdatedNowPlayingInfoUniqueIdentifier->toUInt64() : 0,
manager->hasActiveNowPlayingSession(),
manager->registeredAsNowPlayingApplication(),
manager->haveEverRegisteredAsNowPlayingApplication()
} };
#else
return Exception { ExceptionCode::InvalidAccessError };
#endif
}
void Internals::setNowPlayingUpdateInterval(double interval)
{
if (RefPtr manager = sessionManager())
manager->setNowPlayingUpdateInterval(interval);
}
ExceptionOr<double> Internals::nowPlayingUpdateInterval() const
{
RefPtr manager = sessionManager();
if (!manager)
return Exception { ExceptionCode::InvalidAccessError };
return manager->nowPlayingUpdateInterval();
}
#if ENABLE(VIDEO)
RefPtr<HTMLMediaElement> Internals::bestMediaElementForRemoteControls(Internals::PlaybackControlsPurpose purpose)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return nullptr;
return document->protectedPage()->bestMediaElementForRemoteControls(purpose, document.get());
}
Internals::MediaSessionState Internals::mediaSessionState(HTMLMediaElement& element)
{
return static_cast<Internals::MediaSessionState>(element.mediaSession().state());
}
#endif
ExceptionOr<Internals::MediaUsageState> Internals::mediaUsageState(HTMLMediaElement& element) const
{
#if ENABLE(VIDEO)
element.mediaSession().updateMediaUsageIfChanged();
auto info = element.mediaSession().mediaUsageInfo();
if (!info)
return Exception { ExceptionCode::NotSupportedError };
return { { info.value().mediaURL.string(),
info.value().isPlaying,
info.value().canShowControlsManager,
info.value().canShowNowPlayingControls,
info.value().isSuspended,
info.value().isInActiveDocument,
info.value().isFullscreen,
info.value().isMuted,
info.value().isMediaDocumentInMainFrame,
info.value().isVideo,
info.value().isAudio,
info.value().hasVideo,
info.value().hasAudio,
info.value().hasRenderer,
info.value().audioElementWithUserGesture,
info.value().userHasPlayedAudioBefore,
info.value().isElementRectMostlyInMainFrame,
info.value().playbackPermitted,
info.value().pageMediaPlaybackSuspended,
info.value().isMediaDocumentAndNotOwnerElement,
info.value().pageExplicitlyAllowsElementToAutoplayInline,
info.value().requiresFullscreenForVideoPlaybackAndFullscreenNotPermitted,
info.value().isVideoAndRequiresUserGestureForVideoRateChange,
info.value().isAudioAndRequiresUserGestureForAudioRateChange,
info.value().isVideoAndRequiresUserGestureForVideoDueToLowPowerMode,
info.value().isVideoAndRequiresUserGestureForVideoDueToAggressiveThermalMitigation,
info.value().noUserGestureRequired,
info.value().requiresPlaybackAndIsNotPlaying,
info.value().hasEverNotifiedAboutPlaying,
info.value().outsideOfFullscreen,
info.value().isLargeEnoughForMainContent,
} };
#else
UNUSED_PARAM(element);
return Exception { ExceptionCode::InvalidAccessError };
#endif
}
ExceptionOr<bool> Internals::elementShouldDisplayPosterImage(HTMLVideoElement& element) const
{
#if ENABLE(VIDEO)
return element.shouldDisplayPosterImage();
#else
UNUSED_PARAM(element);
return Exception { ExceptionCode::InvalidAccessError };
#endif
}
#if ENABLE(VIDEO)
size_t Internals::mediaElementCount() const
{
return HTMLMediaElement::allMediaElements().size();
}
void Internals::setMediaElementVolumeLocked(HTMLMediaElement& element, bool volumeLocked)
{
element.setVolumeLocked(volumeLocked);
}
#if ENABLE(SPEECH_SYNTHESIS)
SpeechSynthesisUtterance* Internals::speechSynthesisUtteranceForCue(const VTTCue& cue)
{
return cue.speechUtterance();
}
ExceptionOr<RefPtr<VTTCue>> Internals::mediaElementCurrentlySpokenCue(HTMLMediaElement& element)
{
RefPtr cue = dynamicDowncast<VTTCue>(element.cueBeingSpoken());
ASSERT(cue);
if (!cue)
return Exception { ExceptionCode::InvalidAccessError };
return cue;
}
#endif
bool Internals::elementIsActiveNowPlayingSession(HTMLMediaElement& element) const
{
return element.isActiveNowPlayingSession();
}
#endif // ENABLE(VIDEO)
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
void Internals::setMockMediaPlaybackTargetPickerEnabled(bool enabled)
{
RefPtr frame = this->frame();
if (!frame || !frame->page())
return;
frame->page()->setMockMediaPlaybackTargetPickerEnabled(enabled);
}
ExceptionOr<void> Internals::setMockMediaPlaybackTargetPickerState(const String& deviceName, const String& deviceState)
{
RefPtr frame = this->frame();
if (!frame || !frame->page())
return Exception { ExceptionCode::InvalidAccessError };
MediaPlaybackTargetMock::State state = MediaPlaybackTargetMock::State::Unknown;
if (equalLettersIgnoringASCIICase(deviceState, "deviceavailable"_s))
state = MediaPlaybackTargetMock::State::OutputDeviceAvailable;
else if (equalLettersIgnoringASCIICase(deviceState, "deviceunavailable"_s))
state = MediaPlaybackTargetMock::State::OutputDeviceUnavailable;
else if (equalLettersIgnoringASCIICase(deviceState, "unknown"_s))
state = MediaPlaybackTargetMock::State::Unknown;
else
return Exception { ExceptionCode::InvalidAccessError };
frame->page()->setMockMediaPlaybackTargetPickerState(deviceName, state);
return { };
}
void Internals::mockMediaPlaybackTargetPickerDismissPopup()
{
RefPtr frame = this->frame();
if (!frame || !frame->page())
return;
frame->page()->mockMediaPlaybackTargetPickerDismissPopup();
}
#endif
bool Internals::isMonitoringWirelessRoutes() const
{
#if ENABLE(VIDEO)
if (RefPtr manager = sessionManager())
return manager->isMonitoringWirelessTargets();
#endif
return false;
}
ExceptionOr<Ref<MockPageOverlay>> Internals::installMockPageOverlay(PageOverlayType type)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
return MockPageOverlayClient::singleton().installOverlay(*document->page(), type == PageOverlayType::View ? PageOverlay::OverlayType::View : PageOverlay::OverlayType::Document);
}
ExceptionOr<String> Internals::pageOverlayLayerTreeAsText(unsigned short flags) const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
document->updateLayout(LayoutOptions::IgnorePendingStylesheets);
return MockPageOverlayClient::singleton().layerTreeAsText(*document->page(), toLayerTreeAsTextOptions(flags));
}
void Internals::setPageMuted(StringView statesString)
{
RefPtr document = contextDocument();
if (!document)
return;
WebCore::MediaProducerMutedStateFlags state;
for (StringView stateString : statesString.split(',')) {
if (equalLettersIgnoringASCIICase(stateString, "audio"_s))
state.add(MediaProducerMutedState::AudioIsMuted);
if (equalLettersIgnoringASCIICase(stateString, "capturedevices"_s))
state.add(MediaProducer::AudioAndVideoCaptureIsMuted);
if (equalLettersIgnoringASCIICase(stateString, "screencapture"_s))
state.add(MediaProducerMutedState::ScreenCaptureIsMuted);
}
if (RefPtr page = document->page())
page->setMuted(state);
}
String Internals::pageMediaState()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return emptyString();
auto state = document->page()->mediaState();
StringBuilder string;
if (state.containsAny(MediaProducerMediaState::IsPlayingAudio))
string.append("IsPlayingAudio,"_s);
if (state.containsAny(MediaProducerMediaState::IsPlayingVideo))
string.append("IsPlayingVideo,"_s);
if (state.containsAny(MediaProducerMediaState::IsPlayingToExternalDevice))
string.append("IsPlayingToExternalDevice,"_s);
if (state.containsAny(MediaProducerMediaState::RequiresPlaybackTargetMonitoring))
string.append("RequiresPlaybackTargetMonitoring,"_s);
if (state.containsAny(MediaProducerMediaState::ExternalDeviceAutoPlayCandidate))
string.append("ExternalDeviceAutoPlayCandidate,"_s);
if (state.containsAny(MediaProducerMediaState::DidPlayToEnd))
string.append("DidPlayToEnd,"_s);
if (state.containsAny(MediaProducerMediaState::IsSourceElementPlaying))
string.append("IsSourceElementPlaying,"_s);
if (state.containsAny(MediaProducerMediaState::IsNextTrackControlEnabled))
string.append("IsNextTrackControlEnabled,"_s);
if (state.containsAny(MediaProducerMediaState::IsPreviousTrackControlEnabled))
string.append("IsPreviousTrackControlEnabled,"_s);
if (state.containsAny(MediaProducerMediaState::HasPlaybackTargetAvailabilityListener))
string.append("HasPlaybackTargetAvailabilityListener,"_s);
if (state.containsAny(MediaProducerMediaState::HasAudioOrVideo))
string.append("HasAudioOrVideo,"_s);
if (state.containsAny(MediaProducerMediaState::HasActiveAudioCaptureDevice))
string.append("HasActiveAudioCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasMutedAudioCaptureDevice))
string.append("HasMutedAudioCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasInterruptedAudioCaptureDevice))
string.append("HasInterruptedAudioCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasActiveVideoCaptureDevice))
string.append("HasActiveVideoCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasMutedVideoCaptureDevice))
string.append("HasMutedVideoCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasInterruptedVideoCaptureDevice))
string.append("HasInterruptedVideoCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasUserInteractedWithMediaElement))
string.append("HasUserInteractedWithMediaElement,"_s);
if (state.containsAny(MediaProducerMediaState::HasActiveScreenCaptureDevice))
string.append("HasActiveScreenCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasMutedScreenCaptureDevice))
string.append("HasMutedScreenCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasActiveWindowCaptureDevice))
string.append("HasActiveWindowCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasMutedWindowCaptureDevice))
string.append("HasMutedWindowCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasInterruptedWindowCaptureDevice))
string.append("HasInterruptedWindowCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasActiveSystemAudioCaptureDevice))
string.append("HasActiveSystemAudioCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasMutedSystemAudioCaptureDevice))
string.append("HasMutedSystemAudioCaptureDevice,"_s);
if (state.containsAny(MediaProducerMediaState::HasInterruptedSystemAudioCaptureDevice))
string.append("HasInterruptedSystemAudioCaptureDevice,"_s);
if (string.isEmpty())
string.append("IsNotPlaying"_s);
else
string.shrink(string.length() - 1);
return string.toString();
}
void Internals::setPageDefersLoading(bool defersLoading)
{
RefPtr document = contextDocument();
if (!document)
return;
if (RefPtr page = document->page())
page->setDefersLoading(defersLoading);
}
ExceptionOr<bool> Internals::pageDefersLoading()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::InvalidAccessError };
return document->page()->defersLoading();
}
void Internals::grantUniversalAccess()
{
if (RefPtr document = contextDocument())
document->securityOrigin().grantUniversalAccess();
}
void Internals::disableCORSForURL(const String& url)
{
if (auto* page = contextDocument() ? contextDocument()->page() : nullptr)
page->addCORSDisablingPatternForTesting(UserContentURLPattern { url });
}
RefPtr<File> Internals::createFile(const String& path)
{
RefPtr document = contextDocument();
if (!document)
return nullptr;
URL url = document->completeURL(path);
if (!url.protocolIsFile())
return nullptr;
if (RefPtr page = document->page())
page->chrome().client().registerBlobPathForTesting(url.fileSystemPath(), [] () { });
return File::create(document, url.fileSystemPath());
}
void Internals::asyncCreateFile(const String& path, DOMPromiseDeferred<IDLInterface<File>>&& promise)
{
RefPtr document = contextDocument();
if (!document) {
promise.reject(ExceptionCode::InvalidStateError);
return;
}
URL url = document->completeURL(path);
if (!url.protocolIsFile()) {
promise.reject(ExceptionCode::InvalidStateError);
return;
}
if (RefPtr page = document->page()) {
auto fileSystemPath = url.fileSystemPath();
page->chrome().client().registerBlobPathForTesting(fileSystemPath, [promise = WTF::move(promise), weakDocument = WeakPtr { *document }, url = WTF::move(url)] () mutable {
if (!weakDocument) {
promise.reject(ExceptionCode::InvalidStateError);
return;
}
promise.resolve(File::create(weakDocument.get(), url.fileSystemPath()));
});
} else
promise.reject(ExceptionCode::InvalidStateError);
}
String Internals::createTemporaryFile(const String& name, const String& contents)
{
if (name.isEmpty())
return nullString();
auto [path, file] = FileSystem::openTemporaryFile(makeString("WebCoreTesting-"_s, name));
if (!file)
return nullString();
file.write(byteCast<uint8_t>(contents.utf8().span()));
return path;
}
void Internals::queueMicroTask(int testNumber)
{
RefPtr document = contextDocument();
if (!document)
return;
ScriptExecutionContext* context = document;
auto& eventLoop = context->eventLoop();
eventLoop.queueMicrotask([document = Ref { *document }, testNumber]() {
document->addConsoleMessage(MessageSource::JS, MessageLevel::Debug, makeString("MicroTask #"_s, testNumber, " has run."_s));
});
}
#if ENABLE(CONTENT_FILTERING)
MockContentFilterSettings& Internals::mockContentFilterSettings()
{
return MockContentFilterSettings::singleton();
}
#endif
static void serializeOffset(StringBuilder& builder, const SnapOffset<LayoutUnit>& snapOffset)
{
builder.append(snapOffset.offset.toUnsigned());
if (snapOffset.stop == ScrollSnapStop::Always)
builder.append(" (always)"_s);
}
static void serializeOffsets(StringBuilder& builder, const Vector<SnapOffset<LayoutUnit>>& snapOffsets)
{
builder.append("{ "_s, interleave(snapOffsets, serializeOffset, ", "_s), " }"_s);
}
void Internals::setPlatformMomentumScrollingPredictionEnabled(bool enabled)
{
ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled(enabled);
}
ExceptionOr<String> Internals::scrollSnapOffsets(Element& element)
{
auto areaOrException = scrollableAreaForNode(&element);
if (areaOrException.hasException())
return areaOrException.releaseException();
auto* scrollableArea = areaOrException.releaseReturnValue();
if (!scrollableArea)
return Exception { ExceptionCode::InvalidAccessError };
auto* offsetInfo = scrollableArea->snapOffsetsInfo();
StringBuilder result;
if (offsetInfo && !offsetInfo->horizontalSnapOffsets.isEmpty()) {
result.append("horizontal = "_s);
serializeOffsets(result, offsetInfo->horizontalSnapOffsets);
}
if (offsetInfo && !offsetInfo->verticalSnapOffsets.isEmpty()) {
if (result.length())
result.append(", "_s);
result.append("vertical = "_s);
serializeOffsets(result, offsetInfo->verticalSnapOffsets);
}
return String { result.toString() };
}
ExceptionOr<bool> Internals::isScrollSnapInProgress(Element& element)
{
auto areaOrException = scrollableAreaForNode(&element);
if (areaOrException.hasException())
return areaOrException.releaseException();
auto* scrollableArea = areaOrException.releaseReturnValue();
if (!scrollableArea)
return Exception { ExceptionCode::InvalidAccessError };
return scrollableArea->isScrollSnapInProgress();
}
bool Internals::testPreloaderSettingViewport()
{
return testPreloadScannerViewportSupport(contextDocument());
}
ExceptionOr<String> Internals::pathStringWithShrinkWrappedRects(const Vector<double>& rectComponents, double radius)
{
if (rectComponents.size() % 4)
return Exception { ExceptionCode::InvalidAccessError };
Vector<FloatRect> rects;
for (unsigned i = 0; i < rectComponents.size(); i += 4)
rects.append(FloatRect(rectComponents[i], rectComponents[i + 1], rectComponents[i + 2], rectComponents[i + 3]));
SVGPathStringBuilder builder;
PathUtilities::pathWithShrinkWrappedRects(rects, radius).applyElements([&builder](const PathElement& element) {
switch (element.type) {
case PathElement::Type::MoveToPoint:
builder.moveTo(element.points[0], false, AbsoluteCoordinates);
return;
case PathElement::Type::AddLineToPoint:
builder.lineTo(element.points[0], AbsoluteCoordinates);
return;
case PathElement::Type::AddQuadCurveToPoint:
builder.curveToQuadratic(element.points[0], element.points[1], AbsoluteCoordinates);
return;
case PathElement::Type::AddCurveToPoint:
builder.curveToCubic(element.points[0], element.points[1], element.points[2], AbsoluteCoordinates);
return;
case PathElement::Type::CloseSubpath:
builder.closePath();
return;
}
ASSERT_NOT_REACHED();
});
return builder.result();
}
void Internals::systemBeep()
{
SystemSoundManager::singleton().systemBeep();
}
#if ENABLE(VIDEO)
void Internals::setMediaControlsMaximumRightContainerButtonCountOverride(HTMLMediaElement& mediaElement, size_t count)
{
mediaElement.setMediaControlsMaximumRightContainerButtonCountOverride(count);
}
void Internals::setMediaControlsHidePlaybackRates(HTMLMediaElement& mediaElement, bool hidePlaybackRates)
{
mediaElement.setMediaControlsHidePlaybackRates(hidePlaybackRates);
}
#endif // ENABLE(VIDEO)
void Internals::setPageMediaVolume(float volume)
{
RefPtr document = contextDocument();
if (!document)
return;
RefPtr page = document->page();
if (!page)
return;
page->setMediaVolume(volume);
}
float Internals::pageMediaVolume()
{
RefPtr document = contextDocument();
if (!document)
return 0;
RefPtr page = document->page();
if (!page)
return 0;
return page->mediaVolume();
}
#if !PLATFORM(COCOA)
String Internals::userVisibleString(const DOMURL& url)
{
return WTF::URLHelpers::userVisibleURL(url.href().string().utf8());
}
#endif
void Internals::setShowAllPlugins(bool show)
{
RefPtr document = contextDocument();
if (!document)
return;
RefPtr page = document->page();
if (!page)
return;
page->setShowAllPlugins(show);
}
bool Internals::isReadableStreamDisturbed(ReadableStream& stream)
{
return stream.isDisturbed();
}
void Internals::observeReadableStreamLifetime(ReadableStream& stream)
{
m_observedLiveReadableStreams.add(stream);
}
unsigned Internals::observedLiveReadableStreamCount()
{
return m_observedLiveReadableStreams.computeSize();
}
JSValue Internals::cloneArrayBuffer(JSC::JSGlobalObject& lexicalGlobalObject, JSValue buffer, JSValue srcByteOffset, JSValue srcLength)
{
auto& vm = lexicalGlobalObject.vm();
const Identifier& privateName = builtinNames(vm).cloneArrayBufferPrivateName();
JSValue value;
PropertySlot propertySlot(value, PropertySlot::InternalMethodType::Get);
lexicalGlobalObject.methodTable()->getOwnPropertySlot(&lexicalGlobalObject, &lexicalGlobalObject, privateName, propertySlot);
value = propertySlot.getValue(&lexicalGlobalObject, privateName);
ASSERT(value.isCallable());
JSObject* function = value.getObject();
auto callData = JSC::getCallData(function);
ASSERT(callData.type != JSC::CallData::Type::None);
MarkedArgumentBuffer arguments;
arguments.append(buffer);
arguments.append(srcByteOffset);
arguments.append(srcLength);
ASSERT(!arguments.hasOverflowed());
return JSC::call(&lexicalGlobalObject, function, callData, JSC::jsUndefined(), arguments);
}
String Internals::resourceLoadStatisticsForURL(const DOMURL& url)
{
return ResourceLoadObserver::singleton().statisticsForURL(url.href());
}
void Internals::setTrackingPreventionEnabled(bool enable)
{
DeprecatedGlobalSettings::setTrackingPreventionEnabled(enable);
}
String Internals::composedTreeAsText(Node& node)
{
if (!is<ContainerNode>(node))
return emptyString();
return WebCore::composedTreeAsText(downcast<ContainerNode>(node));
}
bool Internals::isProcessingUserGesture()
{
return UserGestureIndicator::processingUserGesture();
}
void Internals::withUserGesture(Ref<VoidCallback>&& callback)
{
UserGestureIndicator gestureIndicator(IsProcessingUserGesture::Yes, contextDocument());
callback->invoke();
}
void Internals::withoutUserGesture(Ref<VoidCallback>&& callback)
{
UserGestureIndicator gestureIndicator(IsProcessingUserGesture::No, contextDocument());
callback->invoke();
}
bool Internals::userIsInteracting()
{
if (RefPtr document = contextDocument()) {
if (RefPtr page = document->page())
return page->chrome().client().userIsInteracting();
}
return false;
}
bool Internals::hasTransientActivation()
{
if (RefPtr document = contextDocument()) {
if (RefPtr window = document->window())
return window->hasTransientActivation();
}
return false;
}
bool Internals::consumeTransientActivation()
{
if (RefPtr document = contextDocument()) {
if (RefPtr window = document->window())
return window->consumeTransientActivation();
}
return false;
}
double Internals::lastHandledUserGestureTimestamp()
{
RefPtr document = contextDocument();
if (!document)
return 0;
return document->lastHandledUserGestureTimestamp().secondsSinceEpoch().value();
}
bool Internals::hasHistoryActionActivation()
{
if (RefPtr document = contextDocument()) {
if (RefPtr window = document->window())
return window->hasHistoryActionActivation();
}
return false;
}
bool Internals::consumeHistoryActionUserActivation()
{
if (RefPtr document = contextDocument()) {
if (RefPtr window = document->window())
return window->consumeHistoryActionUserActivation();
}
return false;
}
RefPtr<GCObservation> Internals::observeGC(JSC::JSValue value)
{
if (!value.isObject())
return nullptr;
return GCObservation::create(asObject(value));
}
void Internals::setUserInterfaceLayoutDirection(UserInterfaceLayoutDirection userInterfaceLayoutDirection)
{
RefPtr document = contextDocument();
if (!document)
return;
RefPtr page = document->page();
if (!page)
return;
page->setUserInterfaceLayoutDirection(userInterfaceLayoutDirection == UserInterfaceLayoutDirection::LTR ? WebCore::UserInterfaceLayoutDirection::LTR : WebCore::UserInterfaceLayoutDirection::RTL);
}
#if !PLATFORM(COCOA)
bool Internals::userPrefersReducedMotion() const
{
return false;
}
bool Internals::userPrefersContrast() const
{
return false;
}
#if ENABLE(VIDEO)
double Internals::privatePlayerVolume(const HTMLMediaElement&)
{
return 0;
}
bool Internals::privatePlayerMuted(const HTMLMediaElement&)
{
return false;
}
#endif
RefPtr<SharedBuffer> Internals::pngDataForTesting()
{
return nullptr;
}
#endif // !PLATFORM(COCOA)
#if ENABLE(VIDEO)
bool Internals::isMediaElementHidden(const HTMLMediaElement& media)
{
return media.elementIsHidden();
}
double Internals::elementEffectivePlaybackRate(const HTMLMediaElement& media)
{
return media.effectivePlaybackRate();
}
double Internals::privatePlayerCurrentTime(HTMLMediaElement& media)
{
return media.mediaPlayerCurrentTime();
}
#endif
ExceptionOr<void> Internals::setIsPlayingToBluetoothOverride(std::optional<bool> isPlaying)
{
#if ENABLE(ROUTING_ARBITRATION)
AudioSession::singleton().setIsPlayingToBluetoothOverride(isPlaying);
return { };
#else
UNUSED_PARAM(isPlaying);
return Exception { ExceptionCode::NotSupportedError };
#endif
}
void Internals::reportBacktrace()
{
WTFReportBacktrace();
}
void Internals::setBaseWritingDirection(BaseWritingDirection direction)
{
if (RefPtr document = contextDocument()) {
if (RefPtr frame = document->frame()) {
switch (direction) {
case BaseWritingDirection::Ltr:
frame->editor().setBaseWritingDirection(WritingDirection::LeftToRight);
break;
case BaseWritingDirection::Rtl:
frame->editor().setBaseWritingDirection(WritingDirection::RightToLeft);
break;
case BaseWritingDirection::Natural:
frame->editor().setBaseWritingDirection(WritingDirection::Natural);
break;
}
}
}
}
#if ENABLE(POINTER_LOCK)
bool Internals::pageHasPendingPointerLock() const
{
RefPtr document = contextDocument();
if (!document)
return false;
RefPtr page = document->page();
if (!page)
return false;
return page->pointerLockController().lockPending();
}
bool Internals::pageHasPointerLock() const
{
RefPtr document = contextDocument();
if (!document)
return false;
RefPtr page = document->page();
if (!page)
return false;
auto& controller = page->pointerLockController();
return controller.element() && !controller.lockPending();
}
#endif
void Internals::markContextAsInsecure()
{
RefPtr document = contextDocument();
if (!document)
return;
document->securityOrigin().setIsPotentiallyTrustworthy(false);
}
void Internals::postTask(Ref<VoidCallback>&& callback)
{
RefPtr document = contextDocument();
if (!document) {
callback->invoke();
return;
}
document->postTask([callback = WTF::move(callback)](ScriptExecutionContext&) {
callback->invoke();
});
}
static std::optional<TaskSource> taskSourceFromString(const String& taskSourceName)
{
if (taskSourceName == "DOMManipulation"_s)
return TaskSource::DOMManipulation;
return std::nullopt;
}
ExceptionOr<void> Internals::queueTask(ScriptExecutionContext& context, const String& taskSourceName, Ref<VoidCallback>&& callback)
{
auto source = taskSourceFromString(taskSourceName);
if (!source)
return Exception { ExceptionCode::NotSupportedError };
context.eventLoop().queueTask(*source, [callback = WTF::move(callback)] {
callback->invoke();
});
return { };
}
ExceptionOr<void> Internals::queueTaskToQueueMicrotask(Document& document, const String& taskSourceName, Ref<VoidCallback>&& callback)
{
auto source = taskSourceFromString(taskSourceName);
if (!source)
return Exception { ExceptionCode::NotSupportedError };
ScriptExecutionContext& context = document; // This avoids unnecessarily exporting Document::eventLoop.
context.eventLoop().queueTask(*source, [movedCallback = WTF::move(callback), protectedDocument = Ref { document }]() mutable {
ScriptExecutionContext& context = protectedDocument.get();
context.eventLoop().queueMicrotask([callback = WTF::move(movedCallback)] {
callback->invoke();
});
});
return { };
}
ExceptionOr<bool> Internals::hasSameEventLoopAs(WindowProxy& proxy)
{
RefPtr<ScriptExecutionContext> context = contextDocument();
if (!context || !proxy.frame())
return Exception { ExceptionCode::InvalidStateError };
auto* proxyFrame = dynamicDowncast<LocalFrame>(*proxy.frame());
if (!proxyFrame)
return false;
RefPtr<ScriptExecutionContext> proxyContext = proxyFrame->document();
if (!proxyContext)
return Exception { ExceptionCode::InvalidStateError };
return context->eventLoop().hasSameEventLoopAs(proxyContext->eventLoop());
}
Vector<String> Internals::accessKeyModifiers() const
{
Vector<String> accessKeyModifierStrings;
for (auto modifier : EventHandler::accessKeyModifiers()) {
switch (modifier) {
case PlatformEvent::Modifier::AltKey:
accessKeyModifierStrings.append("altKey"_s);
break;
case PlatformEvent::Modifier::ControlKey:
accessKeyModifierStrings.append("ctrlKey"_s);
break;
case PlatformEvent::Modifier::MetaKey:
accessKeyModifierStrings.append("metaKey"_s);
break;
case PlatformEvent::Modifier::ShiftKey:
accessKeyModifierStrings.append("shiftKey"_s);
break;
case PlatformEvent::Modifier::CapsLockKey:
accessKeyModifierStrings.append("capsLockKey"_s);
break;
case PlatformEvent::Modifier::AltGraphKey:
ASSERT_NOT_REACHED(); // AltGraph is only for DOM API.
break;
}
}
return accessKeyModifierStrings;
}
void Internals::setQuickLookPassword(const String& password)
{
#if PLATFORM(IOS_FAMILY) && USE(QUICK_LOOK)
auto& quickLookHandleClient = MockPreviewLoaderClient::singleton();
LegacyPreviewLoader::setClientForTesting(&quickLookHandleClient);
quickLookHandleClient.setPassword(password);
#else
UNUSED_PARAM(password);
#endif
}
void Internals::setAsRunningUserScripts(Document& document)
{
if (RefPtr page = document.page())
page->setHasInjectedUserScript();
}
#if ENABLE(WEBGL)
void Internals::simulateEventForWebGLContext(SimulatedWebGLContextEvent event, WebGLRenderingContext& context)
{
WebGLRenderingContext::SimulatedEventForTesting contextEvent;
switch (event) {
case SimulatedWebGLContextEvent::GPUStatusFailure:
contextEvent = WebGLRenderingContext::SimulatedEventForTesting::GPUStatusFailure;
break;
case SimulatedWebGLContextEvent::Timeout:
contextEvent = WebGLRenderingContext::SimulatedEventForTesting::Timeout;
break;
default:
ASSERT_NOT_REACHED();
return;
}
context.simulateEventForTesting(contextEvent);
}
Internals::RequestedGPU Internals::requestedGPU(WebGLRenderingContext& context)
{
switch (context.creationAttributes().powerPreference) {
case WebGLPowerPreference::Default:
return RequestedGPU::Default;
case WebGLPowerPreference::LowPower:
return RequestedGPU::LowPower;
case WebGLPowerPreference::HighPerformance:
return RequestedGPU::HighPerformance;
}
ASSERT_NOT_REACHED();
return RequestedGPU::Default;
}
#endif
void Internals::setPageVisibility(bool isVisible)
{
updatePageActivityState(ActivityState::IsVisible, isVisible);
}
void Internals::setPageIsFocused(bool isFocused)
{
updatePageActivityState(ActivityState::IsFocused, isFocused);
}
void Internals::setPageIsFocusedAndActive(bool isFocusedAndActive)
{
updatePageActivityState({ ActivityState::IsFocused, ActivityState::WindowIsActive }, isFocusedAndActive);
}
void Internals::setPageIsInWindow(bool isInWindow)
{
updatePageActivityState(ActivityState::IsInWindow, isInWindow);
}
void Internals::updatePageActivityState(OptionSet<ActivityState> statesToChange, bool newValue)
{
RefPtr page = contextDocument() ? contextDocument()->page() : nullptr;
if (!page)
return;
auto state = page->activityState();
if (!newValue)
state.remove(statesToChange);
else
state.add(statesToChange);
page->setActivityState(state);
}
bool Internals::isPageActive() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return false;
auto& page = *document->page();
return page.activityState().contains(ActivityState::WindowIsActive);
}
#if ENABLE(MEDIA_STREAM)
void Internals::setMockAudioTrackChannelNumber(MediaStreamTrack& track, unsigned short channelNumber)
{
if (auto* source = dynamicDowncast<MockRealtimeAudioSource>(track.source()))
source->setChannelCount(channelNumber);
}
void Internals::setCameraMediaStreamTrackOrientation(MediaStreamTrack& track, int orientation)
{
Ref source = track.source();
if (!source->isCaptureSource())
return;
m_orientationNotifier.orientationChanged(orientation);
source->monitorOrientation(m_orientationNotifier);
}
void Internals::stopObservingRealtimeMediaSource()
{
if (!m_trackSource)
return;
switch (m_trackSource->type()) {
case RealtimeMediaSource::Type::Audio:
m_trackSource->removeAudioSampleObserver(*this);
break;
case RealtimeMediaSource::Type::Video:
m_trackSource->removeVideoFrameObserver(*this);
break;
}
m_trackSource->removeObserver(*this);
m_trackSource = nullptr;
m_trackAudioSampleCount = 0;
m_trackVideoSampleCount = 0;
}
void Internals::observeMediaStreamTrack(MediaStreamTrack& track)
{
stopObservingRealtimeMediaSource();
m_trackVideoRotation = -1;
m_trackSource = track.source();
m_trackSource->addObserver(*this);
switch (m_trackSource->type()) {
case RealtimeMediaSource::Type::Audio:
m_trackSource->addAudioSampleObserver(*this);
break;
case RealtimeMediaSource::Type::Video:
m_trackSource->addVideoFrameObserver(*this);
break;
}
}
void Internals::mediaStreamTrackVideoFrameRotation(DOMPromiseDeferred<IDLShort>&& promise)
{
promise.resolve(m_trackVideoRotation);
}
void Internals::videoFrameAvailable(VideoFrame& videoFrame, VideoFrameTimeMetadata)
{
callOnMainThread([this, weakThis = WeakPtr { *this }, videoFrame = Ref { videoFrame }] {
if (!weakThis)
return;
m_trackVideoSampleCount++;
m_trackVideoRotation = static_cast<int>(videoFrame->rotation());
});
}
void Internals::delayMediaStreamTrackSamples(MediaStreamTrack& track, float delay)
{
track.source().delaySamples(Seconds { delay });
}
void Internals::setMediaStreamTrackMuted(MediaStreamTrack& track, bool muted)
{
track.source().setMuted(muted);
}
void Internals::removeMediaStreamTrack(MediaStream& stream, MediaStreamTrack& track)
{
stream.allowEventTracksForTesting();
stream.privateStream().removeTrack(track.privateTrack());
}
void Internals::simulateMediaStreamTrackCaptureSourceFailure(MediaStreamTrack& track)
{
track.source().captureFailed();
}
void Internals::setMediaStreamTrackIdentifier(MediaStreamTrack& track, String&& id)
{
track.setIdForTesting(WTF::move(id));
}
void Internals::setMediaStreamSourceInterrupted(MediaStreamTrack& track, bool interrupted)
{
track.source().setInterruptedForTesting(interrupted);
}
const String& Internals::mediaStreamTrackPersistentId(const MediaStreamTrack& track)
{
return track.source().persistentID();
}
size_t Internals::audioCaptureSourceCount() const
{
if (RefPtr manager = sessionManager())
return manager->audioCaptureSourceCount();
return false;
}
bool Internals::supportsMultiMicrophoneCaptureWithoutEchoCancellation() const
{
#if PLATFORM(MAC)
return true;
#else
return false;
#endif
}
bool Internals::isMediaStreamSourceInterrupted(MediaStreamTrack& track) const
{
return track.source().interrupted();
}
bool Internals::isMediaStreamSourceEnded(MediaStreamTrack& track) const
{
return track.source().isEnded();
}
bool Internals::isMockRealtimeMediaSourceCenterEnabled()
{
return MockRealtimeMediaSourceCenter::mockRealtimeMediaSourceCenterEnabled();
}
bool Internals::shouldAudioTrackPlay(const AudioTrack& track)
{
auto* audioTrack = dynamicDowncast<AudioTrackPrivateMediaStream>(track.privateTrack());
return audioTrack && audioTrack->shouldPlay();
}
#endif // ENABLE(MEDIA_STREAM)
#if ENABLE(WEB_RTC)
String Internals::rtcNetworkInterfaceName() const
{
RefPtr document = contextDocument();
RefPtr rtcNetworkManager = document ? document->rtcNetworkManager() : nullptr;
if (!rtcNetworkManager)
return { };
return rtcNetworkManager->interfaceNameForTesting();
}
#endif // ENABLE(WEB_RTC)
bool Internals::isHardwareVP9DecoderExpected()
{
#if PLATFORM(IOS_FAMILY_SIMULATOR)
return false;
#elif PLATFORM(IOS_FAMILY)
return true;
#elif PLATFORM(MAC) && CPU(ARM64)
return true;
#else
return false;
#endif
}
bool Internals::supportsAudioSession() const
{
#if USE(AUDIO_SESSION)
return true;
#else
return false;
#endif
}
auto Internals::audioSessionCategory() const -> AudioSessionCategory
{
#if USE(AUDIO_SESSION)
return AudioSession::singleton().category();
#else
return AudioSessionCategory::None;
#endif
}
auto Internals::audioSessionMode() const -> AudioSessionMode
{
#if USE(AUDIO_SESSION)
return AudioSession::singleton().mode();
#else
return AudioSessionMode::Default;
#endif
}
auto Internals::routeSharingPolicy() const -> RouteSharingPolicy
{
#if USE(AUDIO_SESSION)
return AudioSession::singleton().routeSharingPolicy();
#else
return RouteSharingPolicy::Default;
#endif
}
#if ENABLE(VIDEO)
auto Internals::categoryAtMostRecentPlayback(HTMLMediaElement& element) const -> AudioSessionCategory
{
#if USE(AUDIO_SESSION)
return element.categoryAtMostRecentPlayback();
#else
UNUSED_PARAM(element);
return AudioSessionCategory::None;
#endif
}
auto Internals::modeAtMostRecentPlayback(HTMLMediaElement& element) const -> AudioSessionMode
{
#if USE(AUDIO_SESSION)
WTFLogAlways("Internals::modeAtMostRecentPlayback");
return element.modeAtMostRecentPlayback();
#else
UNUSED_PARAM(element);
return AudioSessionMode::Default;
#endif
}
#endif
double Internals::preferredAudioBufferSize() const
{
#if USE(AUDIO_SESSION)
return AudioSession::singleton().preferredBufferSize();
#endif
return 0;
}
double Internals::currentAudioBufferSize() const
{
#if USE(AUDIO_SESSION)
return AudioSession::singleton().bufferSize();
#endif
return 0;
}
bool Internals::audioSessionActive() const
{
#if USE(AUDIO_SESSION)
return AudioSession::singleton().isActive();
#endif
return false;
}
void Internals::storeRegistrationsOnDisk(DOMPromiseDeferred<void>&& promise)
{
if (!contextDocument())
return;
Ref connection = ServiceWorkerProvider::singleton().serviceWorkerConnection();
connection->storeRegistrationsOnDiskForTesting([promise = WTF::move(promise)]() mutable {
promise.resolve();
});
}
void Internals::sendH2Ping(String url, DOMPromiseDeferred<IDLDouble>&& promise)
{
RefPtr document = contextDocument();
if (!document) {
promise.reject(ExceptionCode::InvalidStateError);
return;
}
RefPtr frame = document->frame();
if (!frame) {
promise.reject(ExceptionCode::InvalidStateError);
return;
}
frame->loader().client().sendH2Ping(URL { url }, [promise = WTF::move(promise)] (Expected<Seconds, ResourceError>&& result) mutable {
if (result.has_value())
promise.resolve(result.value().value());
else
promise.reject(ExceptionCode::InvalidStateError);
});
}
void Internals::clearCacheStorageMemoryRepresentation(DOMPromiseDeferred<void>&& promise)
{
RefPtr document = contextDocument();
if (!document)
return;
if (!m_cacheStorageConnection) {
if (RefPtr page = document->page())
m_cacheStorageConnection = page->cacheStorageProvider().createCacheStorageConnection();
if (!m_cacheStorageConnection)
return;
}
document->enqueueTaskWhenSettled(m_cacheStorageConnection->clearMemoryRepresentation(ClientOrigin { document->topOrigin().data(), document->securityOrigin().data() }), TaskSource::DOMManipulation, [promise = WTF::move(promise)] (auto&&) mutable {
promise.resolve();
});
}
void Internals::cacheStorageEngineRepresentation(DOMPromiseDeferred<IDLDOMString>&& promise)
{
RefPtr document = contextDocument();
if (!document)
return;
if (!m_cacheStorageConnection) {
if (RefPtr page = document->page())
m_cacheStorageConnection = page->cacheStorageProvider().createCacheStorageConnection();
if (!m_cacheStorageConnection)
return;
}
document->enqueueTaskWhenSettled(m_cacheStorageConnection->engineRepresentation(), TaskSource::DOMManipulation, [promise = WTF::move(promise)](auto&& result) mutable {
if (!result) {
promise.reject(Exception { ExceptionCode::InvalidStateError, "internal error"_s });
return;
}
promise.resolve(WTF::move(result.value()));
});
}
void Internals::updateQuotaBasedOnSpaceUsage()
{
RefPtr document = contextDocument();
if (!document)
return;
if (!m_cacheStorageConnection) {
if (RefPtr page = contextDocument()->page())
m_cacheStorageConnection = page->cacheStorageProvider().createCacheStorageConnection();
if (!m_cacheStorageConnection)
return;
}
m_cacheStorageConnection->updateQuotaBasedOnSpaceUsage(ClientOrigin { document->topOrigin().data(), document->securityOrigin().data() });
}
void Internals::setConsoleMessageListener(RefPtr<StringCallback>&& listener)
{
if (!contextDocument())
return;
if (RefPtr page = contextDocument()->page())
page->setConsoleMessageListenerForTesting(WTF::move(listener));
}
void Internals::setResponseSizeWithPadding(FetchResponse& response, uint64_t size)
{
response.setBodySizeWithPadding(size);
}
uint64_t Internals::responseSizeWithPadding(FetchResponse& response) const
{
return response.bodySizeWithPadding();
}
const String& Internals::responseNetworkLoadMetricsProtocol(const FetchResponse& response)
{
return response.networkLoadMetrics().protocol;
}
void Internals::hasServiceWorkerRegistration(const String& clientURL, HasRegistrationPromise&& promise)
{
if (!contextDocument())
return;
URL parsedURL = contextDocument()->completeURL(clientURL);
return ServiceWorkerProvider::singleton().serviceWorkerConnection().matchRegistration(SecurityOriginData { contextDocument()->topOrigin().data() }, parsedURL, [promise = WTF::move(promise)] (auto&& result) mutable {
promise.resolve(!!result);
});
}
void Internals::terminateServiceWorker(ServiceWorker& worker, DOMPromiseDeferred<void>&& promise)
{
ServiceWorkerProvider::singleton().terminateWorkerForTesting(worker.identifier(), [promise = WTF::move(promise)]() mutable {
promise.resolve();
});
}
void Internals::whenServiceWorkerIsTerminated(ServiceWorker& worker, DOMPromiseDeferred<void>&& promise)
{
return ServiceWorkerProvider::singleton().serviceWorkerConnection().whenServiceWorkerIsTerminatedForTesting(worker.identifier(), [promise = WTF::move(promise)]() mutable {
promise.resolve();
});
}
void Internals::terminateWebContentProcess()
{
exit(0);
}
#if ENABLE(APPLE_PAY)
ExceptionOr<Ref<MockPaymentCoordinator>> Internals::mockPaymentCoordinator(Document& document)
{
RefPtr page = document.page();
if (!page)
return Exception { ExceptionCode::InvalidAccessError };
Ref mockPaymentCoordinator = downcast<MockPaymentCoordinator>(page->paymentCoordinator().client());
return mockPaymentCoordinator;
}
#endif
Internals::ImageOverlayLine::~ImageOverlayLine() = default;
Internals::ImageOverlayText::~ImageOverlayText() = default;
Internals::ImageOverlayBlock::~ImageOverlayBlock() = default;
Internals::ImageOverlayDataDetector::~ImageOverlayDataDetector() = default;
#if ENABLE(IMAGE_ANALYSIS)
template<typename T>
static FloatQuad getQuad(const T& overlayTextOrLine)
{
return {
FloatPoint(overlayTextOrLine.topLeft->x(), overlayTextOrLine.topLeft->y()),
FloatPoint(overlayTextOrLine.topRight->x(), overlayTextOrLine.topRight->y()),
FloatPoint(overlayTextOrLine.bottomRight->x(), overlayTextOrLine.bottomRight->y()),
FloatPoint(overlayTextOrLine.bottomLeft->x(), overlayTextOrLine.bottomLeft->y()),
};
}
static TextRecognitionLineData makeDataForLine(const Internals::ImageOverlayLine& line)
{
return {
getQuad<Internals::ImageOverlayLine>(line),
line.children.map([](auto& textChild) -> TextRecognitionWordData {
return { textChild.text, getQuad(textChild), textChild.hasLeadingWhitespace };
}),
line.hasTrailingNewline,
line.isVertical
};
}
void Internals::requestTextRecognition(Element& element, Ref<VoidCallback>&& callback)
{
auto page = contextDocument()->page();
if (!page)
callback->invoke();
page->chrome().client().requestTextRecognition(element, { }, [callback = WTF::move(callback)] (auto&&) {
callback->invoke();
});
}
RefPtr<Element> Internals::textRecognitionCandidate() const
{
if (RefPtr frame = contextDocument()->frame())
return frame->eventHandler().textRecognitionCandidateElement();
return nullptr;
}
#endif // ENABLE(IMAGE_ANALYSIS)
void Internals::installImageOverlay(Element& element, Vector<ImageOverlayLine>&& lines, Vector<ImageOverlayBlock>&& blocks, Vector<ImageOverlayDataDetector>&& dataDetectors)
{
if (!is<HTMLElement>(element))
return;
#if ENABLE(IMAGE_ANALYSIS)
ImageOverlay::updateWithTextRecognitionResult(downcast<HTMLElement>(element), TextRecognitionResult {
lines.map([] (auto& line) -> TextRecognitionLineData {
return makeDataForLine(line);
})
#if ENABLE(DATA_DETECTION)
, dataDetectors.map([](auto& dataDetector) -> TextRecognitionDataDetector {
return { fakeDataDetectorResultForTesting(), { getQuad(dataDetector) } };
})
#endif // ENABLE(DATA_DETECTION)
, blocks.map([] (auto& block) {
return TextRecognitionBlockData { block.text, getQuad(block) };
})
#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
, TextRecognitionResult::extractAttributedString(fakeImageAnalysisResultForTesting(lines).get())
#endif
});
#else
UNUSED_PARAM(blocks);
UNUSED_PARAM(dataDetectors);
UNUSED_PARAM(lines);
#endif
}
bool Internals::hasActiveDataDetectorHighlight() const
{
#if ENABLE(DATA_DETECTION) && ENABLE(IMAGE_ANALYSIS)
RefPtr document = contextDocument();
if (!document || !document->page())
return false;
if (RefPtr controller = document->page()->imageOverlayControllerIfExists())
return controller->hasActiveDataDetectorHighlightForTesting();
#endif
return false;
}
bool Internals::isSystemPreviewLink(Element& element) const
{
#if USE(SYSTEM_PREVIEW)
auto* anchor = dynamicDowncast<HTMLAnchorElement>(element);
return anchor && anchor->isSystemPreviewLink();
#else
UNUSED_PARAM(element);
return false;
#endif
}
bool Internals::isSystemPreviewImage(Element& element) const
{
#if USE(SYSTEM_PREVIEW)
if (auto* image = dynamicDowncast<HTMLImageElement>(element))
return image->isSystemPreviewImage();
if (auto* picture = dynamicDowncast<HTMLPictureElement>(element))
return picture->isSystemPreviewImage();
return false;
#else
UNUSED_PARAM(element);
return false;
#endif
}
bool Internals::usingAppleInternalSDK() const
{
#if USE(APPLE_INTERNAL_SDK)
return true;
#else
return false;
#endif
}
bool Internals::usingGStreamer() const
{
#if USE(GSTREAMER)
return true;
#else
return false;
#endif
}
void Internals::setCaptureExtraNetworkLoadMetricsEnabled(bool value)
{
platformStrategies()->loaderStrategy()->setCaptureExtraNetworkLoadMetricsEnabled(value);
}
String Internals::ongoingLoadsDescriptions() const
{
StringBuilder builder;
builder.append('[');
bool isStarting = true;
for (auto& identifier : platformStrategies()->loaderStrategy()->ongoingLoads()) {
if (isStarting)
isStarting = false;
else
builder.append(',');
builder.append('[');
for (auto& info : platformStrategies()->loaderStrategy()->intermediateLoadInformationFromResourceLoadIdentifier(identifier))
builder.append('[', (int)info.type, ",\""_s, info.request.url().string(), "\",\""_s, info.request.httpMethod(), "\","_s, info.response.httpStatusCode(), ']');
builder.append(']');
}
builder.append(']');
return builder.toString();
}
void Internals::reloadWithoutContentExtensions()
{
if (RefPtr frame = this->frame())
frame->loader().reload(ReloadOption::DisableContentBlockers);
}
void Internals::disableContentExtensionsChecks()
{
RefPtr frame = this->frame();
RefPtr loader = frame ? frame->loader().documentLoader() : nullptr;
if (loader)
loader->setContentExtensionEnablement({ ContentExtensionDefaultEnablement::Disabled, { } });
}
size_t Internals::pluginCount()
{
if (!contextDocument() || !contextDocument()->page())
return 0;
return contextDocument()->page()->pluginData().webVisiblePlugins().size();
}
static std::optional<ScrollPosition> scrollPositionForPlugin(Element& element)
{
auto* pluginElement = dynamicDowncast<HTMLPlugInElement>(element);
if (RefPtr pluginViewBase = pluginElement ? pluginElement->pluginWidget() : nullptr)
return pluginViewBase->scrollPositionForTesting();
return std::nullopt;
}
ExceptionOr<unsigned> Internals::pluginScrollPositionX(Element& element)
{
if (std::optional scrollPosition = scrollPositionForPlugin(element))
return scrollPosition->x();
return Exception { ExceptionCode::InvalidAccessError };
}
ExceptionOr<unsigned> Internals::pluginScrollPositionY(Element& element)
{
if (std::optional scrollPosition = scrollPositionForPlugin(element))
return scrollPosition->y();
return Exception { ExceptionCode::InvalidAccessError };
}
void Internals::notifyResourceLoadObserver()
{
ResourceLoadObserver::singleton().updateCentralStatisticsStore([] { });
}
unsigned Internals::primaryScreenDisplayID()
{
#if PLATFORM(COCOA)
return WebCore::primaryScreenDisplayID();
#else
return 0;
#endif
}
bool Internals::capsLockIsOn()
{
return WebCore::PlatformKeyboardEvent::currentCapsLockState();
}
auto Internals::parseHEVCCodecParameters(StringView string) -> std::optional<HEVCParameterSet>
{
return WebCore::parseHEVCCodecParameters(string);
}
String Internals::createHEVCCodecParametersString(const HEVCParameterSet& parameters)
{
return WebCore::createHEVCCodecParametersString(parameters);
}
auto Internals::parseDoViCodecParameters(StringView string) -> std::optional<DoViParameterSet>
{
auto parseResult = WebCore::parseDoViCodecParameters(string);
if (!parseResult)
return std::nullopt;
DoViParameterSet convertedResult;
switch (parseResult->codec) {
case DoViParameters::Codec::AVC1:
convertedResult.codecName = "avc1"_s;
break;
case DoViParameters::Codec::AVC3:
convertedResult.codecName = "avc3"_s;
break;
case DoViParameters::Codec::HEV1:
convertedResult.codecName = "hev1"_s;
break;
case DoViParameters::Codec::HVC1:
convertedResult.codecName = "hvc1"_s;
break;
}
convertedResult.bitstreamProfileID = parseResult->bitstreamProfileID;
convertedResult.bitstreamLevelID = parseResult->bitstreamLevelID;
return convertedResult;
}
String Internals::createDoViCodecParametersString(const DoViParameterSet& parameterSet)
{
DoViParameters::Codec codec;
if (parameterSet.codecName == "avc1"_s)
codec = DoViParameters::Codec::AVC1;
else if (parameterSet.codecName == "avc3"_s)
codec = DoViParameters::Codec::AVC3;
else if (parameterSet.codecName == "hev1"_s)
codec = DoViParameters::Codec::HEV1;
else if (parameterSet.codecName == "hvc1"_s)
codec = DoViParameters::Codec::HVC1;
else
return emptyString();
return WebCore::createDoViCodecParametersString({ codec, parameterSet.bitstreamProfileID, parameterSet.bitstreamLevelID });
}
std::optional<VPCodecConfigurationRecord> Internals::parseVPCodecParameters(StringView string)
{
return WebCore::parseVPCodecParameters(string);
}
std::optional<AV1CodecConfigurationRecord> Internals::parseAV1CodecParameters(const String& parameters)
{
return WebCore::parseAV1CodecParameters(parameters);
}
String Internals::createAV1CodecParametersString(const AV1CodecConfigurationRecord& configuration)
{
return WebCore::createAV1CodecParametersString(configuration);
}
bool Internals::validateAV1PerLevelConstraints(const String& parameters, VideoConfiguration&& configuration)
{
if (auto record = WebCore::parseAV1CodecParameters(parameters))
return WebCore::validateAV1PerLevelConstraints(*record, toPlatform(WTF::move(configuration)));
return false;
}
bool Internals::validateAV1ConfigurationRecord(const String& parameters)
{
if (auto record = WebCore::parseAV1CodecParameters(parameters))
return WebCore::validateAV1ConfigurationRecord(*record);
return false;
}
void Internals::setCookie(CookieData&& cookieData)
{
RefPtr document = contextDocument();
if (!document)
return;
RefPtr page = document->page();
if (!page)
return;
page->cookieJar().setRawCookie(*document, CookieData::toCookie(WTF::move(cookieData)), ShouldPartitionCookie::No);
}
auto Internals::getCookies() const -> Vector<CookieData>
{
RefPtr document = contextDocument();
if (!document)
return { };
RefPtr page = document->page();
if (!page)
return { };
Vector<Cookie> cookies;
page->cookieJar().getRawCookies(*document, document->cookieURL(), cookies);
return WTF::map(cookies, [](auto& cookie) {
return CookieData::fromCookie(cookie);
});
}
auto Internals::webDriverGetCookies(Document& document) const -> Vector<WebDriverCookieData>
{
RefPtr page = document.page();
if (!page)
return { };
Vector<Cookie> cookies;
page->cookieJar().getRawCookies(document, document.cookieURL(), cookies);
return WTF::map(cookies, [](auto& cookie) {
return WebDriverCookieData { cookie };
});
}
void Internals::setAlwaysAllowLocalWebarchive(bool alwaysAllowLocalWebarchive)
{
auto* localFrame = frame();
if (!localFrame)
return;
localFrame->loader().setAlwaysAllowLocalWebarchive(alwaysAllowLocalWebarchive);
}
void Internals::processWillSuspend()
{
#if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
if (RefPtr manager = sessionManager())
manager->processWillSuspend();
#endif
}
void Internals::processDidResume()
{
#if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
if (RefPtr manager = sessionManager())
manager->processDidResume();
#endif
}
void Internals::testDictionaryLogging()
{
RefPtr document = contextDocument();
if (!document)
return;
RefPtr page = document->page();
if (!page)
return;
DiagnosticLoggingClient::ValueDictionary dictionary;
dictionary.set("stringKey"_s, String("stringValue"_s));
dictionary.set("uint64Key"_s, std::numeric_limits<uint64_t>::max());
dictionary.set("int64Key"_s, std::numeric_limits<int64_t>::min());
dictionary.set("boolKey"_s, true);
dictionary.set("doubleKey"_s, 2.7182818284590452353602874);
page->diagnosticLoggingClient().logDiagnosticMessageWithValueDictionary("testMessage"_s, "testDescription"_s, dictionary, ShouldSample::No);
}
void Internals::setMaximumIntervalForUserGestureForwardingForFetch(double interval)
{
UserGestureToken::setMaximumIntervalForUserGestureForwardingForFetchForTesting(Seconds(interval));
}
void Internals::setTransientActivationDuration(double seconds)
{
LocalDOMWindow::overrideTransientActivationDurationForTesting(Seconds { seconds });
}
void Internals::setIsPlayingToAutomotiveHeadUnit(bool isPlaying)
{
#if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
if (RefPtr manager = sessionManager())
manager->setIsPlayingToAutomotiveHeadUnit(isPlaying);
#else
UNUSED_PARAM(isPlaying);
#endif
}
String Internals::highlightPseudoElementColor(const AtomString& highlightName, Element& element)
{
element.document().updateStyleIfNeeded();
Ref styleResolver = element.document().styleScope().resolver();
auto* parentStyle = element.computedStyle();
if (!parentStyle)
return { };
auto resolvedStyle = styleResolver->styleForPseudoElement(element, { PseudoElementType::Highlight, highlightName }, { parentStyle });
if (!resolvedStyle)
return { };
return serializationForCSS(resolvedStyle->style->color());
}
Internals::TextIndicatorInfo::TextIndicatorInfo()
{
}
Internals::TextIndicatorInfo::TextIndicatorInfo(const WebCore::TextIndicatorData& data)
: textBoundingRectInRootViewCoordinates(DOMRect::create(data.textBoundingRectInRootViewCoordinates))
, textRectsInBoundingRectCoordinates(DOMRectList::create(data.textRectsInBoundingRectCoordinates))
{
}
Internals::TextIndicatorInfo::~TextIndicatorInfo() = default;
Internals::TextIndicatorInfo Internals::textIndicatorForRange(const Range& range, TextIndicatorOptions options)
{
auto indicator = TextIndicator::createWithRange(makeSimpleRange(range), options.coreOptions(), TextIndicatorPresentationTransition::None);
return indicator->data();
}
void Internals::addPrefetchLoadEventListener(HTMLLinkElement& link, RefPtr<EventListener>&& listener)
{
if (link.document().settings().linkPrefetchEnabled() && equalLettersIgnoringASCIICase(link.rel(), "prefetch"_s)) {
link.allowPrefetchLoadAndErrorForTesting();
link.addEventListener(eventNames().loadEvent, listener.releaseNonNull(), false);
}
}
#if ENABLE(WEB_AUTHN)
void Internals::setMockWebAuthenticationConfiguration(const MockWebAuthenticationConfiguration& configuration)
{
RefPtr document = contextDocument();
if (!document)
return;
RefPtr page = document->page();
if (!page)
return;
page->chrome().client().setMockWebAuthenticationConfiguration(configuration);
}
#endif
void Internals::setMaxCanvasArea(unsigned size)
{
CanvasBase::setMaxCanvasAreaForTesting(size);
}
int Internals::processIdentifier() const
{
return getCurrentProcessID();
}
Ref<InternalsMapLike> Internals::createInternalsMapLike()
{
return InternalsMapLike::create();
}
Ref<InternalsSetLike> Internals::createInternalsSetLike()
{
return InternalsSetLike::create();
}
bool Internals::hasSandboxMachLookupAccessToGlobalName(const String& process, const String& service)
{
#if PLATFORM(COCOA)
pid_t pid;
if (process == "com.apple.WebKit.WebContent"_s)
pid = getpid();
else
RELEASE_ASSERT_NOT_REACHED();
return !sandbox_check(pid, "mach-lookup", static_cast<enum sandbox_filter_type>(SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT), service.utf8().data());
#else
UNUSED_PARAM(process);
UNUSED_PARAM(service);
return false;
#endif
}
bool Internals::hasSandboxMachLookupAccessToXPCServiceName(const String& process, const String& service)
{
#if PLATFORM(COCOA)
pid_t pid;
if (process == "com.apple.WebKit.WebContent"_s)
pid = getpid();
else
RELEASE_ASSERT_NOT_REACHED();
return !sandbox_check(pid, "mach-lookup", static_cast<enum sandbox_filter_type>(SANDBOX_FILTER_XPC_SERVICE_NAME | SANDBOX_CHECK_NO_REPORT), service.utf8().data());
#else
UNUSED_PARAM(process);
UNUSED_PARAM(service);
return false;
#endif
}
bool Internals::hasSandboxUnixSyscallAccess(const String& process, unsigned syscall) const
{
#if PLATFORM(COCOA)
RELEASE_ASSERT(process == "com.apple.WebKit.WebContent"_s);
auto pid = getpid();
return !sandbox_check(pid, "syscall-unix", static_cast<enum sandbox_filter_type>(SANDBOX_FILTER_SYSCALL_NUMBER | SANDBOX_CHECK_NO_REPORT), syscall);
#else
UNUSED_PARAM(process);
UNUSED_PARAM(syscall);
return false;
#endif
}
String Internals::windowLocationHost(DOMWindow& window)
{
return window.location().host();
}
void Internals::setNavigationRateLimiterParameters(DOMWindow& window, unsigned maxNavigations, double windowDurationSeconds)
{
if (RefPtr localWindow = dynamicDowncast<LocalDOMWindow>(window))
localWindow->navigation().rateLimiterForTesting().setParametersForTesting(maxNavigations, Seconds(windowDurationSeconds));
}
void Internals::resetNavigationRateLimiter(DOMWindow& window)
{
if (RefPtr localWindow = dynamicDowncast<LocalDOMWindow>(window))
localWindow->navigation().rateLimiterForTesting().resetForTesting();
}
ExceptionOr<String> Internals::systemColorForCSSValue(const String& cssValue, bool useDarkModeAppearance, bool useElevatedUserInterfaceLevel)
{
CSSValueID id = cssValueKeywordID(cssValue);
if (!CSS::isSystemColorKeyword(id))
return Exception { ExceptionCode::InvalidAccessError };
OptionSet<StyleColorOptions> options;
if (useDarkModeAppearance)
options.add(StyleColorOptions::UseDarkAppearance);
if (useElevatedUserInterfaceLevel)
options.add(StyleColorOptions::UseElevatedUserInterfaceLevel);
return serializationForCSS(RenderTheme::singleton().systemColor(id, options));
}
bool Internals::systemHasBattery() const
{
#if PLATFORM(COCOA)
return WebCore::systemHasBattery();
#else
return false;
#endif
}
void Internals::setSystemHasBatteryForTesting(bool hasBattery)
{
#if PLATFORM(COCOA)
SystemBatteryStatusTestingOverrides::singleton().setHasBattery(hasBattery);
#else
UNUSED_PARAM(hasBattery);
#endif
}
void Internals::setSystemHasACForTesting(bool hasAC)
{
#if PLATFORM(COCOA)
SystemBatteryStatusTestingOverrides::singleton().setHasAC(hasAC);
#else
UNUSED_PARAM(hasAC);
#endif
}
void Internals::setHardwareVP9DecoderDisabledForTesting(bool disabled)
{
#if ENABLE(VP9) && PLATFORM(COCOA)
VP9TestingOverrides::singleton().setHardwareDecoderDisabled(disabled);
#else
UNUSED_PARAM(disabled);
#endif
}
void Internals::setVP9DecoderDisabledForTesting(bool disabled)
{
#if ENABLE(VP9) && PLATFORM(COCOA)
VP9TestingOverrides::singleton().setVP9DecoderDisabled(disabled);
#else
UNUSED_PARAM(disabled);
#endif
}
void Internals::setVP9ScreenSizeAndScaleForTesting(double width, double height, double scale)
{
#if ENABLE(VP9) && PLATFORM(COCOA)
VP9TestingOverrides::singleton().setVP9ScreenSizeAndScale(ScreenDataOverrides { width, height, scale });
#else
UNUSED_PARAM(width);
UNUSED_PARAM(height);
UNUSED_PARAM(scale);
#endif
}
int Internals::readPreferenceInteger(const String& domain, const String& key)
{
#if PLATFORM(COCOA)
Boolean keyExistsAndHasValidFormat = false;
return CFPreferencesGetAppIntegerValue(key.createCFString().get(), domain.createCFString().get(), &keyExistsAndHasValidFormat);
#else
UNUSED_PARAM(domain);
UNUSED_PARAM(key);
return -1;
#endif
}
#if !PLATFORM(COCOA)
String Internals::encodedPreferenceValue(const String&, const String&)
{
return emptyString();
}
bool Internals::isRemoteUIAppForAccessibility()
{
return false;
}
bool Internals::hasSandboxIOKitOpenAccessToClass(const String& process, const String& ioKitClass)
{
UNUSED_PARAM(process);
UNUSED_PARAM(ioKitClass);
return false;
}
#endif
#if ENABLE(APP_HIGHLIGHTS)
Vector<String> Internals::appHighlightContextMenuItemTitles() const
{
return {{
contextMenuItemTagAddHighlightToCurrentQuickNote(),
contextMenuItemTagAddHighlightToNewQuickNote(),
}};
}
unsigned Internals::numberOfAppHighlights()
{
RefPtr document = contextDocument();
if (!document)
return 0;
auto appHighlightRegistry = document->appHighlightRegistryIfExists();
if (!appHighlightRegistry)
return 0;
unsigned numHighlights = 0;
for (auto& highlight : appHighlightRegistry->map())
numHighlights += highlight.value->highlightRanges().size();
return numHighlights;
}
#endif
Vector<Ref<AbstractRange>> Internals::textExtractionHighlightRanges() const
{
RefPtr document = contextDocument();
if (!document)
return { };
RefPtr registry = document->textExtractionHighlightRegistryIfExists();
if (!registry)
return { };
return flatMap(copyToVector(registry->map().values()), [](auto&& highlight) {
return highlight->highlightRanges().map([](auto& range) {
return Ref { range->range() };
});
});
}
bool Internals::supportsPictureInPicture()
{
return WebCore::supportsPictureInPicture();
}
String Internals::focusRingColor()
{
return serializationForCSS(RenderTheme::singleton().focusRingColor(StyleColorOptions::UseSystemAppearance));
}
ExceptionOr<unsigned> Internals::createSleepDisabler(const String& reason, bool display)
{
RefPtr document = contextDocument();
if (!document || !document->pageID())
return Exception { ExceptionCode::InvalidAccessError };
static unsigned lastUsedIdentifier = 0;
auto sleepDisabler = makeUnique<WebCore::SleepDisabler>(reason, display ? PAL::SleepDisabler::Type::Display : PAL::SleepDisabler::Type::System, *document->pageID());
m_sleepDisablers.add(++lastUsedIdentifier, WTF::move(sleepDisabler));
return lastUsedIdentifier;
}
bool Internals::destroySleepDisabler(unsigned identifier)
{
return m_sleepDisablers.remove(identifier);
}
#if ENABLE(WEBXR)
ExceptionOr<Ref<WebXRTest>> Internals::xrTest()
{
RefPtr document = contextDocument();
if (!document || !document->window() || !document->settings().webXREnabled())
return Exception { ExceptionCode::InvalidAccessError };
if (!m_xrTest) {
auto* navigator = contextDocument()->window()->optionalNavigator();
if (!navigator)
return Exception { ExceptionCode::InvalidAccessError };
m_xrTest = WebXRTest::create(NavigatorWebXR::xr(*navigator));
}
return Ref<WebXRTest> { *m_xrTest };
}
#endif
#if ENABLE(ENCRYPTED_MEDIA)
unsigned Internals::mediaKeysInternalInstanceObjectRefCount(const MediaKeys& mediaKeys) const
{
return mediaKeys.internalInstanceObjectRefCount();
}
unsigned Internals::mediaKeySessionInternalInstanceSessionObjectRefCount(const MediaKeySession& mediaKeySession) const
{
return mediaKeySession.internalInstanceSessionObjectRefCount();
}
#endif
void Internals::setContentSizeCategory(Internals::ContentSizeCategory category)
{
#if PLATFORM(IOS) || PLATFORM(VISION)
CFStringRef ctCategory = nil;
switch (category) {
case Internals::ContentSizeCategory::L:
ctCategory = kCTFontContentSizeCategoryL;
break;
case Internals::ContentSizeCategory::XXXL:
ctCategory = kCTFontContentSizeCategoryXXXL;
break;
}
WebCore::setContentSizeCategory(ctCategory);
Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
#else
UNUSED_PARAM(category);
#endif
}
#if ENABLE(ATTACHMENT_ELEMENT)
#if ENABLE(SERVICE_CONTROLS)
bool Internals::hasImageControls(const HTMLImageElement& element) const
{
return ImageControlsMac::hasImageControls(element);
}
#endif // ENABLE(SERVICE_CONTROLS)
String Internals::attachmentElementShadowUserAgentStyleSheet() const
{
return HTMLAttachmentElement::shadowUserAgentStyleSheetText();
}
#endif // ENABLE(ATTACHMENT_ELEMENT)
#if ENABLE(MEDIA_SESSION)
ExceptionOr<double> Internals::currentMediaSessionPosition(const MediaSession& session)
{
if (auto currentPosition = session.currentPosition())
return *currentPosition;
return Exception { ExceptionCode::InvalidStateError };
}
ExceptionOr<void> Internals::sendMediaSessionAction(MediaSession& session, const MediaSessionActionDetails& actionDetails)
{
if (session.callActionHandler(actionDetails))
return { };
return Exception { ExceptionCode::InvalidStateError };
}
#if ENABLE(WEB_CODECS)
void Internals::loadArtworkImage(String&& url, ArtworkImagePromise&& promise)
{
if (!contextDocument()) {
promise.reject(Exception { ExceptionCode::InvalidStateError, "No document."_s });
return;
}
if (m_artworkImagePromise) {
promise.reject(Exception { ExceptionCode::InvalidStateError, "Another download is currently pending."_s });
return;
}
m_artworkImagePromise = makeUnique<ArtworkImagePromise>(WTF::move(promise));
m_artworkLoader = ArtworkImageLoader::create(*contextDocument(), url, [weakThis = WeakPtr { *this }](Image* image) {
RefPtr protectedThis = weakThis.get();
if (!protectedThis)
return;
RefPtr document = protectedThis->contextDocument();
if (!document)
return;
auto promise = std::exchange(protectedThis->m_artworkImagePromise, { });
RefPtr nativeImage = image ? image->nativeImage() : nullptr;
if (!nativeImage) {
promise->reject(Exception { ExceptionCode::InvalidAccessError, "No image retrieved."_s });
return;
}
promise->settle(WebCodecsVideoFrame::create(*document, *nativeImage));
});
m_artworkLoader->requestImageResource();
}
#endif
ExceptionOr<Vector<String>> Internals::platformSupportedCommands() const
{
RefPtr manager = sessionManager();
if (!manager || !contextDocument())
return Exception { ExceptionCode::InvalidAccessError };
auto commands = manager->supportedCommands();
Vector<String> commandStrings;
for (auto command : commands)
commandStrings.append(convertEnumerationToString(command));
return commandStrings;
}
#endif
#if ENABLE(MEDIA_SESSION_COORDINATOR)
ExceptionOr<void> Internals::registerMockMediaSessionCoordinator(ScriptExecutionContext& context, Ref<StringCallback>&& listener)
{
if (m_mockMediaSessionCoordinator)
return { };
RefPtr document = contextDocument();
if (!document || !document->window())
return Exception { ExceptionCode::InvalidAccessError };
if (!document->settings().mediaSessionCoordinatorEnabled())
return Exception { ExceptionCode::InvalidAccessError };
Ref session = NavigatorMediaSession::mediaSession(document->window()->navigator());
auto mock = MockMediaSessionCoordinator::create(context, WTF::move(listener));
m_mockMediaSessionCoordinator = mock.ptr();
session->coordinator().setMediaSessionCoordinatorPrivate(WTF::move(mock));
return { };
}
ExceptionOr<void> Internals::setMockMediaSessionCoordinatorCommandsShouldFail(bool shouldFail)
{
if (!m_mockMediaSessionCoordinator)
return Exception { ExceptionCode::InvalidStateError };
m_mockMediaSessionCoordinator->setCommandsShouldFail(shouldFail);
return { };
}
#endif // ENABLE(MEDIA_SESSION)
constexpr ASCIILiteral string(std::partial_ordering ordering)
{
if (is_lt(ordering))
return "less"_s;
if (is_gt(ordering))
return "greater"_s;
if (is_eq(ordering))
return "equivalent"_s;
return "unordered"_s;
}
constexpr TreeType convertType(Internals::TreeType type)
{
switch (type) {
case Internals::Tree:
return Tree;
case Internals::ShadowIncludingTree:
return ShadowIncludingTree;
case Internals::ComposedTree:
return ComposedTree;
}
ASSERT_NOT_REACHED();
return Tree;
}
String Internals::treeOrder(Node& a, Node& b, TreeType type)
{
return string(treeOrderForTesting(convertType(type), a, b));
}
String Internals::treeOrderBoundaryPoints(Node& containerA, unsigned offsetA, Node& containerB, unsigned offsetB, TreeType type)
{
return string(treeOrderForTesting(convertType(type), BoundaryPoint( containerA, offsetA ), BoundaryPoint(containerB, offsetB)));
}
bool Internals::rangeContainsNode(const AbstractRange& range, Node& node, TreeType type)
{
return contains(convertType(type), makeSimpleRange(range), node);
}
bool Internals::rangeContainsBoundaryPoint(const AbstractRange& range, Node& container, unsigned offset, TreeType type)
{
return contains(convertType(type), makeSimpleRange(range), { container, offset });
}
bool Internals::rangeContainsRange(const AbstractRange& outerRange, const AbstractRange& innerRange, TreeType type)
{
return contains(convertType(type), makeSimpleRange(outerRange), makeSimpleRange(innerRange));
}
bool Internals::rangeIntersectsNode(const AbstractRange& range, Node& node, TreeType type)
{
return intersectsForTesting(convertType(type), makeSimpleRange(range), node);
}
bool Internals::rangeIntersectsRange(const AbstractRange& a, const AbstractRange& b, TreeType type)
{
return intersectsForTesting(convertType(type), makeSimpleRange(a), makeSimpleRange(b));
}
String Internals::dumpStyleResolvers()
{
RefPtr document = contextDocument();
if (!document || !document->window())
return { };
document->updateStyleIfNeeded();
unsigned currentIdentifier = 0;
HashMap<Style::Resolver*, unsigned> resolverIdentifiers;
StringBuilder result;
auto dumpResolver = [&](auto name, auto& resolver) {
auto identifier = resolverIdentifiers.ensure(&resolver, [&] {
return currentIdentifier++;
}).iterator->value;
result.append('(', name, ' ', "(identifier="_s, identifier, ") (author rule count="_s,
resolver.ruleSets().authorStyle().ruleCount(), "))\n"_s);
};
dumpResolver("document resolver"_s, document->styleScope().resolver());
for (Ref shadowRoot : document->inDocumentShadowRoots()) {
auto name = shadowRoot->mode() == ShadowRootMode::UserAgent ? "shadow root resolver (user agent)"_s : "shadow root resolver (author)"_s;
dumpResolver(name, const_cast<ShadowRoot&>(shadowRoot.get()).styleScope().resolver());
}
return result.toString();
}
ExceptionOr<void> Internals::setDocumentAutoplayPolicy(Document& document, Internals::AutoplayPolicy policy)
{
static_assert(static_cast<uint8_t>(WebCore::AutoplayPolicy::Default) == static_cast<uint8_t>(Internals::AutoplayPolicy::Default), "Internals::Default != WebCore::Default");
static_assert(static_cast<uint8_t>(WebCore::AutoplayPolicy::Allow) == static_cast<uint8_t>(Internals::AutoplayPolicy::Allow), "Internals::Allow != WebCore::Allow");
static_assert(static_cast<uint8_t>(WebCore::AutoplayPolicy::AllowWithoutSound) == static_cast<uint8_t>(Internals::AutoplayPolicy::AllowWithoutSound), "Internals::AllowWithoutSound != WebCore::AllowWithoutSound");
static_assert(static_cast<uint8_t>(WebCore::AutoplayPolicy::Deny) == static_cast<uint8_t>(Internals::AutoplayPolicy::Deny), "Internals::Deny != WebCore::Deny");
RefPtr loader = document.loader();
if (!loader)
return Exception { ExceptionCode::InvalidStateError };
loader->setAutoplayPolicy(static_cast<WebCore::AutoplayPolicy>(policy));
return { };
}
void Internals::retainTextIteratorForDocumentContent()
{
RefPtr document = contextDocument();
if (!document)
return;
auto range = makeRangeSelectingNodeContents(*document);
m_textIterator = makeUnique<TextIterator>(range);
}
RefPtr<PushSubscription> Internals::createPushSubscription(const String& endpoint, std::optional<EpochTimeStamp> expirationTime, const ArrayBuffer& serverVAPIDPublicKey, const ArrayBuffer& clientECDHPublicKey, const ArrayBuffer& auth)
{
return PushSubscription::create(PushSubscriptionData { std::nullopt, { endpoint }, expirationTime, serverVAPIDPublicKey.toVector(), clientECDHPublicKey.toVector(), auth.toVector() });
}
#if ENABLE(ARKIT_INLINE_PREVIEW_MAC)
void Internals::modelInlinePreviewUUIDs(ModelInlinePreviewUUIDsPromise&& promise) const
{
RefPtr document = contextDocument();
if (!document) {
promise.reject(ExceptionCode::InvalidStateError);
return;
}
RefPtr frame = document->frame();
if (!frame) {
promise.reject(ExceptionCode::InvalidStateError);
return;
}
CompletionHandler<void(Vector<String>&&)> completionHandler = [promise = WTF::move(promise)] (Vector<String> uuids) mutable {
promise.resolve(uuids);
};
frame->loader().client().modelInlinePreviewUUIDs(WTF::move(completionHandler));
}
String Internals::modelInlinePreviewUUIDForModelElement(const HTMLModelElement& modelElement) const
{
return modelElement.inlinePreviewUUIDForTesting();
}
#endif
bool Internals::hasSleepDisabler() const
{
RefPtr document = contextDocument();
return document ? document->hasSleepDisabler() : false;
}
void Internals::acceptTypedArrays(Int32Array&)
{
// Do nothing.
}
Internals::SelectorFilterHashCounts Internals::selectorFilterHashCounts(const String& selector)
{
auto selectorList = CSSSelectorParser::parseSelectorList(selector, CSSParserContext(*contextDocument()));
if (!selectorList)
return { };
auto hashes = SelectorFilter::collectHashesForTesting(selectorList->first());
return { hashes.ids.size(), hashes.classes.size(), hashes.tags.size(), hashes.attributes.size() };
}
bool Internals::isVisuallyNonEmpty() const
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return false;
auto* frameView = document->frame()->view();
return frameView && frameView->isVisuallyNonEmpty();
}
bool Internals::isUsingUISideCompositing() const
{
RefPtr document = contextDocument();
if (!document)
return false;
RefPtr page = document->page();
if (!page)
return false;
return page->chrome().client().isUsingUISideCompositing();
}
AccessibilityObject* Internals::axObjectForElement(Element& element) const
{
RefPtr document = contextDocument();
if (!document)
return nullptr;
WebCore::AXObjectCache::enableAccessibility();
if (CheckedPtr cache = document->axObjectCache()) {
cache->performDeferredCacheUpdate(ForceLayout::No);
return cache->exportedGetOrCreate(element);
}
return nullptr;
}
String Internals::getComputedLabel(Element& element) const
{
// Force a layout. If we don't, and this request has come in before the render tree was built,
// the accessibility object for this element will not be created (because it doesn't yet have its renderer).
updateLayoutAndStyleForAllFrames();
RefPtr axObject = axObjectForElement(element);
return axObject ? axObject->computedLabel() : ""_s;
}
String Internals::getComputedRole(Element& element) const
{
updateLayoutAndStyleForAllFrames();
RefPtr axObject = axObjectForElement(element);
return axObject ? axObject->computedRoleString() : ""_s;
}
bool Internals::hasScopeBreakingHasSelectors() const
{
contextDocument()->styleScope().flushPendingUpdate();
return !!contextDocument()->styleScope().resolver().ruleSets().scopeBreakingHasPseudoClassInvalidationRuleSet();
}
void Internals::setHistoryTotalStateObjectPayloadLimitOverride(uint32_t limit)
{
RefPtr window = contextDocument() ? contextDocument()->window() : nullptr;
if (!window)
return;
window->history().setTotalStateObjectPayloadLimitOverride(limit);
}
void Internals::setPDFDisplayModeForTesting(Element& element, const String& displayMode) const
{
RefPtr pluginElement = dynamicDowncast<HTMLPlugInElement>(element);
if (!pluginElement)
return;
RefPtr pluginViewBase = pluginElement->pluginWidget();
if (!pluginViewBase)
return;
pluginViewBase->setPDFDisplayModeForTesting(displayMode);
}
void Internals::unlockPDFDocumentForTesting(Element& element, const String& password) const
{
RefPtr pluginElement = dynamicDowncast<HTMLPlugInElement>(element);
if (!pluginElement)
return;
RefPtr pluginViewBase = pluginElement->pluginWidget();
if (!pluginViewBase)
return;
pluginViewBase->unlockPDFDocumentForTesting(password);
}
bool Internals::sendEditingCommandToPDFForTesting(Element& element, const String& commandName, const String& argument) const
{
RefPtr pluginElement = dynamicDowncast<HTMLPlugInElement>(element);
if (!pluginElement)
return false;
RefPtr pluginViewBase = pluginElement->pluginWidget();
if (!pluginViewBase)
return false;
return pluginViewBase->sendEditingCommandToPDFForTesting(commandName, argument);
}
Vector<Internals::PDFAnnotationRect> Internals::pdfAnnotationRectsForTesting(Element& element) const
{
Vector<PDFAnnotationRect> annotationRects;
if (RefPtr pluginElement = dynamicDowncast<HTMLPlugInElement>(element)) {
if (RefPtr pluginViewBase = pluginElement ? pluginElement->pluginWidget() : nullptr) {
for (auto& annotationRect : pluginViewBase->pdfAnnotationRectsForTesting())
annotationRects.append({ annotationRect.x(), annotationRect.y(), annotationRect.width(), annotationRect.height() });
}
}
return annotationRects;
}
void Internals::setPDFTextAnnotationValueForTesting(Element& element, unsigned pageIndex, unsigned annotationIndex, const String& value)
{
RefPtr pluginElement = dynamicDowncast<HTMLPlugInElement>(element);
if (!pluginElement)
return;
RefPtr pluginView = pluginElement->pluginWidget();
if (!pluginView)
return;
pluginView->setPDFTextAnnotationValueForTesting(pageIndex, annotationIndex, value);
}
void Internals::registerPDFTest(Ref<VoidCallback>&& callback, Element& element)
{
RefPtr pluginElement = dynamicDowncast<HTMLPlugInElement>(element);
if (!pluginElement)
return;
if (RefPtr pluginViewBase = pluginElement->pluginWidget())
pluginViewBase->registerPDFTestCallback(WTF::move(callback));
}
String Internals::defaultSpatialTrackingLabel() const
{
#if HAVE(SPATIAL_TRACKING_LABEL)
RefPtr document = contextDocument();
if (!document)
return nullString();
if (RefPtr page = document->page())
return page->defaultSpatialTrackingLabel();
#endif
return nullString();
}
#if ENABLE(VIDEO)
bool Internals::isEffectivelyMuted(const HTMLMediaElement& element)
{
return element.effectiveMuted();
}
Ref<EventTarget> Internals::addInternalEventTarget(HTMLMediaElement& element)
{
return EventTargetForTesting::create(element.document(), element);
}
#endif
std::optional<RenderingMode> Internals::getEffectiveRenderingModeOfNewlyCreatedAcceleratedImageBuffer()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return std::nullopt;
if (RefPtr imageBuffer = ImageBuffer::create({ 100, 100 }, RenderingMode::Accelerated, RenderingPurpose::DOM, 1, DestinationColorSpace::SRGB(), PixelFormat::BGRA8, &document->page()->chrome())) {
imageBuffer->ensureBackendCreated();
if (imageBuffer->hasBackend())
return imageBuffer->renderingMode();
}
return std::nullopt;
}
void Internals::getImageBufferResourceLimits(ImageBufferResourceLimitsPromise&& promise)
{
RefPtr document = contextDocument();
if (!document || !document->page()) {
promise.reject(Exception { ExceptionCode::InvalidStateError });
return;
}
document->page()->chrome().client().getImageBufferResourceLimitsForTesting([promise = WTF::move(promise)](auto&& limits) mutable {
if (!limits) {
promise.reject(Exception { ExceptionCode::InvalidStateError });
return;
}
promise.resolve(*limits);
});
}
void Internals::setResourceCachingDisabledByWebInspector(bool disabled)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return;
document->page()->setResourceCachingDisabledByWebInspector(disabled);
}
ExceptionOr<void> Internals::lowerAllFrameMemoryMonitorLimits()
{
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->frameMemoryMonitor().lowerAllMemoryLimitsForTesting();
return { };
}
void Internals::setTopDocumentURLForQuirks(const String& urlString)
{
RefPtr document = contextDocument();
if (!document || !document->page())
return;
document->protectedPage()->settings().setNeedsSiteSpecificQuirks(true);
document->quirks().setTopDocumentURLForTesting(URL { urlString });
}
#if ENABLE(CONTENT_EXTENSIONS)
void Internals::setResourceMonitorNetworkUsageThreshold(size_t threshold, double randomness)
{
ResourceMonitorChecker::singleton().setNetworkUsageThreshold(threshold, randomness);
}
bool Internals::shouldSkipResourceMonitorThrottling() const
{
if (RefPtr document = contextDocument())
return document->shouldSkipResourceMonitorThrottling();
return false;
}
void Internals::setShouldSkipResourceMonitorThrottling(bool flag)
{
if (RefPtr document = contextDocument())
document->setShouldSkipResourceMonitorThrottling(flag);
}
#endif
#if ENABLE(DAMAGE_TRACKING)
ExceptionOr<Vector<Internals::FrameDamage>> Internals::getFrameDamageHistory() const
{
RefPtr document = contextDocument();
if (!document || !document->page())
return Exception { ExceptionCode::NotSupportedError };
Vector<Internals::FrameDamage> damageDetails;
unsigned sequenceId = 0;
document->page()->chrome().client().foreachRegionInDamageHistoryForTesting([&](const auto& region) {
const auto& regionBounds = region.bounds();
damageDetails.append(FrameDamage {
.sequenceId = sequenceId++,
.bounds = DOMRectReadOnly::create(regionBounds.x(), regionBounds.y(), regionBounds.width(), regionBounds.height()),
.rects = region.rects().map([](const IntRect& rect) -> Ref<DOMRectReadOnly> {
return DOMRectReadOnly::create(rect.x(), rect.y(), rect.width(), rect.height());
}),
});
});
return damageDetails;
}
#endif
RefPtr<MediaSessionManagerInterface> Internals::sessionManager() const
{
RefPtr document = contextDocument();
if (!document)
return nullptr;
RefPtr page = document->page();
if (!page)
return nullptr;
return page->mediaSessionManager();
}
bool Internals::hasMediaSessionManager() const
{
RefPtr document = contextDocument();
if (!document)
return false;
RefPtr page = document->page();
if (!page)
return false;
return !!page->mediaSessionManagerIfExists();
}
size_t Internals::fileConnectionHandleCount(const FileSystemHandle& handle) const
{
return handle.connectionHandleCount();
}
static void storeNextResults(DOMAsyncIterator& iterator, Vector<JSC::Strong<JSC::Unknown>>&& results, Internals::IteratorResultPromise&& promise)
{
iterator.callNext([iterator = Ref { iterator }, results = WTF::move(results), promise = WTF::move(promise)](auto* globalObject, bool isOK, JSC::JSValue value) mutable {
if (!globalObject) {
promise.reject(Exception { ExceptionCode::InvalidStateError });
return;
}
if (!isOK) {
promise.rejectWithCallback([value](auto&) {
return value;
});
return;
}
Ref vm = globalObject->vm();
results.append({ vm.get(), value });
auto scope = DECLARE_THROW_SCOPE(vm);
auto done = value.get(globalObject, vm->propertyNames->done);
if (scope.exception()) {
promise.reject(Exception { ExceptionCode::ExistingExceptionError });
return;
}
if (done.toBoolean(globalObject)) {
promise.resolve(WTF::move(results));
return;
}
storeNextResults(iterator.get(), WTF::move(results), WTF::move(promise));
});
}
void Internals::testAsyncIterator(JSDOMGlobalObject& globalObject, JSC::JSValue value, IteratorResultPromise&& promise)
{
auto domIteratorOrException = DOMAsyncIterator::create(globalObject, value);
if (domIteratorOrException.hasException()) {
promise.reject(domIteratorOrException.releaseException());
return;
}
Vector<JSC::Strong<JSC::Unknown>> results;
Ref domIterator = domIteratorOrException.releaseReturnValue();
storeNextResults(domIterator.get(), WTF::move(results), WTF::move(promise));
}
ExceptionOr<Ref<ReadableStream>> Internals::readableStreamFromMessagePort(JSDOMGlobalObject& globalObject, MessagePort& port)
{
return setupCrossRealmTransformReadable(globalObject, port);
}
#if ENABLE(MODEL_ELEMENT)
void Internals::disableModelLoadDelaysForTesting()
{
RefPtr document = contextDocument();
if (!document || !document->page())
return;
document->page()->disableModelLoadDelaysForTesting();
}
String Internals::modelElementState(HTMLModelElement& element)
{
return element.modelElementStateForTesting();
}
bool Internals::isModelElementIntersectingViewport(HTMLModelElement& element)
{
return element.isIntersectingViewport();
}
#endif
// FIXME: Implement this method for iOS.
ExceptionOr<void> Internals::copyImageAtLocation(int x, int y)
{
#if !PLATFORM(IOS_FAMILY)
RefPtr document = contextDocument();
if (!document || !document->frame())
return Exception { ExceptionCode::InvalidAccessError };
document->updateLayout(LayoutOptions::IgnorePendingStylesheets);
constexpr OptionSet<HitTestRequest::Type> hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::DisallowUserAgentShadowContent, HitTestRequest::Type::AllowChildFrameContent };
RefPtr localFrame = dynamicDowncast<LocalFrame>(document->frame()->mainFrame());
if (!localFrame)
return Exception { ExceptionCode::InvalidAccessError };
auto hitTestResult = localFrame->eventHandler().hitTestResultAtPoint(IntPoint(x, y), hitType);
localFrame->protectedEditor()->copyImage(hitTestResult);
#endif
UNUSED_PARAM(x);
UNUSED_PARAM(y);
return { };
}
#if ENABLE(WIRELESS_PLAYBACK_MEDIA_PLAYER)
MockMediaDeviceRouteController& Internals::mockMediaDeviceRouteController()
{
if (!m_mockMediaDeviceRouteController)
m_mockMediaDeviceRouteController = MockMediaDeviceRouteController::create();
return *m_mockMediaDeviceRouteController;
}
#endif
} // namespace WebCore