| /* |
| * 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 |