source: webkit/trunk/Source/WebCore/testing/Internals.cpp

Last change on this file was 295718, checked in by Matt Woodrow, 3 years ago

Don't allocate device pixel ratio scaled backing stores for layers that contain only unscaled bitmap content
https://bugs.webkit.org/show_bug.cgi?id=241450

Reviewed by Simon Fraser.

  • Source/WebCore/platform/graphics/GraphicsLayer.cpp:

(WebCore::GraphicsLayer::GraphicsLayer):

  • Source/WebCore/platform/graphics/GraphicsLayer.h:

(WebCore::GraphicsLayer::setAppliesDeviceScale):
(WebCore::GraphicsLayer::appliesDeviceScale const):
(WebCore::GraphicsLayer::deviceScaleFactor const):

  • Source/WebCore/rendering/RenderLayerBacking.cpp:

(WebCore::PaintedContentsInfo::isUnscaledBitmapOnly):
(WebCore::PaintedContentsInfo::contentsTypeDetermination):
(WebCore::RenderLayerBacking::updateConfiguration):
(WebCore::RenderLayerBacking::isUnscaledBitmapOnly const):

  • Source/WebCore/rendering/RenderLayerBacking.h:

Adds isUnscaledBitmapOnly to check if a layer contains only a <canvas>/image,
and no other rasterized content, and that the bitmap is drawn unscaled.
If so, uses setAppliesDeviceScale to disable hidpi backing stores for this layer.

  • Source/WebCore/platform/graphics/LayerTreeAsTextOptions.h:
  • Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp:

(WebCore::GraphicsLayerCA::dumpAdditionalProperties const):

  • Source/WebCore/testing/Internals.cpp:

(WebCore::toLayerTreeAsTextOptions):

  • Source/WebCore/testing/Internals.h:
  • Source/WebCore/testing/Internals.idl:

Adds new internals.LAYER_TREE_INCLUDES_DEVICE_SCALE flag for layer tree dumping, in order
to test whether a layer has a high dpi backing store or not.

  • LayoutTests/compositing/canvas/hidpi-canvas-backing-store-expected.txt: Added.
  • LayoutTests/compositing/canvas/hidpi-canvas-backing-store-invalidation-expected.txt: Added.
  • LayoutTests/compositing/canvas/hidpi-canvas-backing-store-invalidation.html: Added.
  • LayoutTests/compositing/canvas/hidpi-canvas-backing-store.html: Added.

Adds new test for low-dpi backing store for a layer containing only a canvas, and an invalidation
test to check that we go back to hidpi when we add a rasterized outline.

Canonical link: https://commits.webkit.org/251723@main

  • Property svn:eol-style set to native
File size: 215.3 KB
Line 
1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 * Copyright (C) 2013-2022 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "Internals.h"
29
30#include "AXObjectCache.h"
31#include "AddEventListenerOptions.h"
32#include "AnimationTimeline.h"
33#include "ApplicationCacheStorage.h"
34#include "AudioSession.h"
35#include "AudioTrackPrivateMediaStream.h"
36#include "Autofill.h"
37#include "BackForwardCache.h"
38#include "BackForwardController.h"
39#include "BitmapImage.h"
40#include "Blob.h"
41#include "CSSKeyframesRule.h"
42#include "CSSMediaRule.h"
43#include "CSSPropertyParser.h"
44#include "CSSStyleRule.h"
45#include "CSSSupportsRule.h"
46#include "CacheStorageConnection.h"
47#include "CacheStorageProvider.h"
48#include "CachedImage.h"
49#include "CachedResourceLoader.h"
50#include "CertificateInfo.h"
51#include "Chrome.h"
52#include "ChromeClient.h"
53#include "ClientOrigin.h"
54#include "ColorSerialization.h"
55#include "ComposedTreeIterator.h"
56#include "CookieJar.h"
57#include "CrossOriginPreflightResultCache.h"
58#include "Cursor.h"
59#include "DOMPointReadOnly.h"
60#include "DOMRect.h"
61#include "DOMRectList.h"
62#include "DOMStringList.h"
63#include "DOMURL.h"
64#include "DOMWindow.h"
65#include "DeprecatedGlobalSettings.h"
66#include "DiagnosticLoggingClient.h"
67#include "DisabledAdaptations.h"
68#include "DisplayList.h"
69#include "Document.h"
70#include "DocumentLoader.h"
71#include "DocumentMarkerController.h"
72#include "DocumentTimeline.h"
73#include "DocumentTimelinesController.h"
74#include "Editor.h"
75#include "Element.h"
76#include "ElementRareData.h"
77#include "EventHandler.h"
78#include "EventListener.h"
79#include "EventLoop.h"
80#include "EventNames.h"
81#include "ExtendableEvent.h"
82#include "ExtensionStyleSheets.h"
83#include "FetchRequest.h"
84#include "FetchResponse.h"
85#include "File.h"
86#include "FloatQuad.h"
87#include "FontCache.h"
88#include "FormController.h"
89#include "Frame.h"
90#include "FrameLoader.h"
91#include "FrameView.h"
92#include "FullscreenManager.h"
93#include "GCObservation.h"
94#include "GridPosition.h"
95#include "HEVCUtilities.h"
96#include "HTMLAnchorElement.h"
97#include "HTMLAttachmentElement.h"
98#include "HTMLCanvasElement.h"
99#include "HTMLIFrameElement.h"
100#include "HTMLImageElement.h"
101#include "HTMLInputElement.h"
102#include "HTMLLinkElement.h"
103#include "HTMLNames.h"
104#include "HTMLPictureElement.h"
105#include "HTMLPlugInElement.h"
106#include "HTMLPreloadScanner.h"
107#include "HTMLSelectElement.h"
108#include "HTMLTextAreaElement.h"
109#include "HTMLVideoElement.h"
110#include "HighlightRegister.h"
111#include "HistoryController.h"
112#include "HistoryItem.h"
113#include "HitTestResult.h"
114#include "IDBRequest.h"
115#include "IDBTransaction.h"
116#include "ImageOverlay.h"
117#include "ImageOverlayController.h"
118#include "InlineIteratorLineBox.h"
119#include "InspectorClient.h"
120#include "InspectorController.h"
121#include "InspectorDebuggableType.h"
122#include "InspectorFrontendClientLocal.h"
123#include "InspectorOverlay.h"
124#include "InstrumentingAgents.h"
125#include "IntRect.h"
126#include "InternalSettings.h"
127#include "InternalsMapLike.h"
128#include "InternalsSetLike.h"
129#include "JSDOMPromiseDeferred.h"
130#include "JSImageData.h"
131#include "LegacySchemeRegistry.h"
132#include "LibWebRTCProvider.h"
133#include "LoaderStrategy.h"
134#include "LocalizedStrings.h"
135#include "Location.h"
136#include "MallocStatistics.h"
137#include "MediaDevices.h"
138#include "MediaEngineConfigurationFactory.h"
139#include "MediaKeySession.h"
140#include "MediaKeys.h"
141#include "MediaMetadata.h"
142#include "MediaPlayer.h"
143#include "MediaProducer.h"
144#include "MediaRecorderProvider.h"
145#include "MediaResourceLoader.h"
146#include "MediaSession.h"
147#include "MediaSessionActionDetails.h"
148#include "MediaStreamTrack.h"
149#include "MediaUsageInfo.h"
150#include "MemoryCache.h"
151#include "MemoryInfo.h"
152#include "MockAudioDestinationCocoa.h"
153#include "MockLibWebRTCPeerConnection.h"
154#include "MockPageOverlay.h"
155#include "MockPageOverlayClient.h"
156#include "ModalContainerObserver.h"
157#include "NavigatorBeacon.h"
158#include "NavigatorMediaDevices.h"
159#include "NetworkLoadInformation.h"
160#include "Page.h"
161#include "PageOverlay.h"
162#include "PathUtilities.h"
163#include "PictureInPictureSupport.h"
164#include "PlatformKeyboardEvent.h"
165#include "PlatformMediaSession.h"
166#include "PlatformMediaSessionManager.h"
167#include "PlatformScreen.h"
168#include "PlatformStrategies.h"
169#include "PluginData.h"
170#include "PrintContext.h"
171#include "PseudoElement.h"
172#include "PushSubscription.h"
173#include "PushSubscriptionData.h"
174#include "RTCRtpSFrameTransform.h"
175#include "Range.h"
176#include "ReadableStream.h"
177#include "RenderEmbeddedObject.h"
178#include "RenderLayerBacking.h"
179#include "RenderLayerCompositor.h"
180#include "RenderLayerScrollableArea.h"
181#include "RenderListBox.h"
182#include "RenderMenuList.h"
183#include "RenderTheme.h"
184#include "RenderThemeIOS.h"
185#include "RenderTreeAsText.h"
186#include "RenderView.h"
187#include "RenderedDocumentMarker.h"
188#include "ResourceLoadObserver.h"
189#include "RuntimeEnabledFeatures.h"
190#include "SMILTimeContainer.h"
191#include "SVGDocumentExtensions.h"
192#include "SVGPathStringBuilder.h"
193#include "SVGSVGElement.h"
194#include "SWClientConnection.h"
195#include "ScriptController.h"
196#include "ScriptedAnimationController.h"
197#include "ScrollingCoordinator.h"
198#include "ScrollingMomentumCalculator.h"
199#include "SecurityOrigin.h"
200#include "SerializedScriptValue.h"
201#include "ServiceWorker.h"
202#include "ServiceWorkerProvider.h"
203#include "ServiceWorkerRegistration.h"
204#include "ServiceWorkerRegistrationData.h"
205#include "Settings.h"
206#include "ShadowRoot.h"
207#include "SourceBuffer.h"
208#include "SpellChecker.h"
209#include "StaticNodeList.h"
210#include "StorageNamespace.h"
211#include "StorageNamespaceProvider.h"
212#include "StringCallback.h"
213#include "StyleResolver.h"
214#include "StyleRule.h"
215#include "StyleScope.h"
216#include "StyleSheetContents.h"
217#include "SystemSoundManager.h"
218#include "TextIterator.h"
219#include "TextPainter.h"
220#include "TextPlaceholderElement.h"
221#include "TextRecognitionOptions.h"
222#include "ThreadableBlobRegistry.h"
223#include "TreeScope.h"
224#include "TypeConversions.h"
225#include "UserGestureIndicator.h"
226#include "UserMediaController.h"
227#include "ViewportArguments.h"
228#include "VoidCallback.h"
229#include "WebAnimation.h"
230#include "WebAnimationUtilities.h"
231#include "WebCoreJSClientData.h"
232#include "WindowProxy.h"
233#include "WorkerThread.h"
234#include "WorkletGlobalScope.h"
235#include "WritingDirection.h"
236#include "XMLHttpRequest.h"
237#include <JavaScriptCore/CodeBlock.h>
238#include <JavaScriptCore/InspectorAgentBase.h>
239#include <JavaScriptCore/InspectorFrontendChannel.h>
240#include <JavaScriptCore/JSCInlines.h>
241#include <JavaScriptCore/JSCJSValue.h>
242#include <wtf/FileSystem.h>
243#include <wtf/HexNumber.h>
244#include <wtf/JSONValues.h>
245#include <wtf/Language.h>
246#include <wtf/MemoryPressureHandler.h>
247#include <wtf/MonotonicTime.h>
248#include <wtf/ProcessID.h>
249#include <wtf/URLHelpers.h>
250#include <wtf/text/StringBuilder.h>
251#include <wtf/text/StringConcatenateNumbers.h>
252#include <wtf/text/StringToIntegerConversion.h>
253
254#if USE(CG)
255#include "PDFDocumentImage.h"
256#endif
257
258#if ENABLE(INPUT_TYPE_COLOR)
259#include "ColorChooser.h"
260#endif
261
262#if ENABLE(MOUSE_CURSOR_SCALE)
263#include <wtf/dtoa.h>
264#endif
265
266#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
267#include "LegacyCDM.h"
268#include "LegacyMockCDM.h"
269#endif
270
271#if ENABLE(ENCRYPTED_MEDIA)
272#include "MockCDMFactory.h"
273#endif
274
275#if ENABLE(VIDEO)
276#include "CaptionUserPreferences.h"
277#include "HTMLMediaElement.h"
278#include "PageGroup.h"
279#include "TextTrack.h"
280#include "TextTrackCueGeneric.h"
281#include "TimeRanges.h"
282#endif
283
284#if ENABLE(WEBGL)
285#include "WebGLRenderingContext.h"
286#endif
287
288#if ENABLE(SPEECH_SYNTHESIS)
289#include "DOMWindowSpeechSynthesis.h"
290#include "PlatformSpeechSynthesizerMock.h"
291#include "SpeechSynthesis.h"
292#endif
293
294#if ENABLE(MEDIA_STREAM)
295#include "MediaStream.h"
296#include "MockRealtimeMediaSourceCenter.h"
297#endif
298
299#if ENABLE(MEDIA_RECORDER)
300#include "MediaRecorder.h"
301#include "MediaRecorderPrivateMock.h"
302#endif
303
304#if ENABLE(WEB_RTC)
305#include "RTCPeerConnection.h"
306#endif
307
308#if ENABLE(MEDIA_SOURCE)
309#include "MockMediaPlayerMediaSource.h"
310#endif
311
312#if ENABLE(CONTENT_FILTERING)
313#include "MockContentFilterSettings.h"
314#endif
315
316#if ENABLE(WEB_AUDIO)
317#include "AudioContext.h"
318#endif
319
320#if ENABLE(WIRELESS_PLAYBACK_TARGET)
321#include "MediaPlaybackTargetContext.h"
322#endif
323
324#if ENABLE(POINTER_LOCK)
325#include "PointerLockController.h"
326#endif
327
328#if USE(QUICK_LOOK)
329#include "LegacyPreviewLoader.h"
330#include "MockPreviewLoaderClient.h"
331#endif
332
333#if ENABLE(APPLE_PAY)
334#include "MockPaymentCoordinator.h"
335#include "PaymentCoordinator.h"
336#endif
337
338#if ENABLE(WEBXR)
339#include "NavigatorWebXR.h"
340#include "WebXRSystem.h"
341#include "WebXRTest.h"
342#endif
343
344#if PLATFORM(MAC)
345#include "GraphicsChecksMac.h"
346#include "NSScrollerImpDetails.h"
347#include "ScrollbarThemeMac.h"
348#endif
349
350#if PLATFORM(IOS_FAMILY)
351#include "FontCacheCoreText.h"
352#include "MediaSessionHelperIOS.h"
353#endif
354
355#if PLATFORM(COCOA)
356#include "SystemBattery.h"
357#include "VP9UtilitiesCocoa.h"
358#include <pal/spi/cf/CoreTextSPI.h>
359#include <wtf/spi/darwin/SandboxSPI.h>
360#endif
361
362#if ENABLE(MEDIA_SESSION_COORDINATOR)
363#include "MediaSessionCoordinator.h"
364#include "MockMediaSessionCoordinator.h"
365#include "NavigatorMediaSession.h"
366#endif
367
368#if ENABLE(MEDIA_SESSION) && USE(GLIB)
369#include "MediaSessionManagerGLib.h"
370#endif
371
372#if ENABLE(IMAGE_ANALYSIS)
373#include "TextRecognitionResult.h"
374#endif
375
376#if ENABLE(ARKIT_INLINE_PREVIEW_MAC)
377#include "HTMLModelElement.h"
378#endif
379
380#if ENABLE(SERVICE_CONTROLS)
381#include "ImageControlsMac.h"
382#endif
383
384using JSC::CallData;
385using JSC::CodeBlock;
386using JSC::FunctionExecutable;
387using JSC::Identifier;
388using JSC::JSFunction;
389using JSC::JSGlobalObject;
390using JSC::JSObject;
391using JSC::JSValue;
392using JSC::MarkedArgumentBuffer;
393using JSC::PropertySlot;
394using JSC::ScriptExecutable;
395using JSC::StackVisitor;
396
397namespace WebCore {
398
399using namespace Inspector;
400using namespace HTMLNames;
401
402class InspectorStubFrontend final : public InspectorFrontendClientLocal, public FrontendChannel {
403public:
404 InspectorStubFrontend(Page& inspectedPage, RefPtr<DOMWindow>&& frontendWindow);
405 virtual ~InspectorStubFrontend();
406
407private:
408 bool supportsDockSide(DockSide) final { return false; }
409 void attachWindow(DockSide) final { }
410 void detachWindow() final { }
411 void closeWindow() final;
412 void reopen() final { }
413 void bringToFront() final { }
414 void setForcedAppearance(InspectorFrontendClient::Appearance) final { }
415 String localizedStringsURL() const final { return String(); }
416 DebuggableType debuggableType() const final { return DebuggableType::Page; }
417 String targetPlatformName() const { return "Unknown"_s; }
418 String targetBuildVersion() const { return "Unknown"_s; }
419 String targetProductVersion() const { return "Unknown"_s; }
420 bool targetIsSimulator() const { return false; }
421 void inspectedURLChanged(const String&) final { }
422 void showCertificate(const CertificateInfo&) final { }
423 void setAttachedWindowHeight(unsigned) final { }
424 void setAttachedWindowWidth(unsigned) final { }
425 void setSheetRect(const FloatRect&) final { }
426
427 void sendMessageToFrontend(const String& message) final;
428 ConnectionType connectionType() const final { return ConnectionType::Local; }
429
430 RefPtr<DOMWindow> m_frontendWindow;
431};
432
433InspectorStubFrontend::InspectorStubFrontend(Page& inspectedPage, RefPtr<DOMWindow>&& frontendWindow)
434 : InspectorFrontendClientLocal(&inspectedPage.inspectorController(), frontendWindow->document()->page(), makeUnique<InspectorFrontendClientLocal::Settings>())
435 , m_frontendWindow(frontendWindow.copyRef())
436{
437 ASSERT_ARG(frontendWindow, frontendWindow);
438
439 frontendPage()->inspectorController().setInspectorFrontendClient(this);
440 inspectedPage.inspectorController().connectFrontend(*this);
441}
442
443InspectorStubFrontend::~InspectorStubFrontend()
444{
445 closeWindow();
446}
447
448void InspectorStubFrontend::closeWindow()
449{
450 if (!m_frontendWindow)
451 return;
452
453 frontendPage()->inspectorController().setInspectorFrontendClient(nullptr);
454 inspectedPage()->inspectorController().disconnectFrontend(*this);
455
456 m_frontendWindow->close();
457 m_frontendWindow = nullptr;
458}
459
460void InspectorStubFrontend::sendMessageToFrontend(const String& message)
461{
462 frontendAPIDispatcher().dispatchMessageAsync(message);
463}
464
465static bool markerTypeFrom(const String& markerType, DocumentMarker::MarkerType& result)
466{
467 if (equalLettersIgnoringASCIICase(markerType, "spelling"_s))
468 result = DocumentMarker::Spelling;
469 else if (equalLettersIgnoringASCIICase(markerType, "grammar"_s))
470 result = DocumentMarker::Grammar;
471 else if (equalLettersIgnoringASCIICase(markerType, "textmatch"_s))
472 result = DocumentMarker::TextMatch;
473 else if (equalLettersIgnoringASCIICase(markerType, "replacement"_s))
474 result = DocumentMarker::Replacement;
475 else if (equalLettersIgnoringASCIICase(markerType, "correctionindicator"_s))
476 result = DocumentMarker::CorrectionIndicator;
477 else if (equalLettersIgnoringASCIICase(markerType, "rejectedcorrection"_s))
478 result = DocumentMarker::RejectedCorrection;
479 else if (equalLettersIgnoringASCIICase(markerType, "autocorrected"_s))
480 result = DocumentMarker::Autocorrected;
481 else if (equalLettersIgnoringASCIICase(markerType, "spellcheckingexemption"_s))
482 result = DocumentMarker::SpellCheckingExemption;
483 else if (equalLettersIgnoringASCIICase(markerType, "deletedautocorrection"_s))
484 result = DocumentMarker::DeletedAutocorrection;
485 else if (equalLettersIgnoringASCIICase(markerType, "dictationalternatives"_s))
486 result = DocumentMarker::DictationAlternatives;
487#if ENABLE(TELEPHONE_NUMBER_DETECTION)
488 else if (equalLettersIgnoringASCIICase(markerType, "telephonenumber"_s))
489 result = DocumentMarker::TelephoneNumber;
490#endif
491 else
492 return false;
493
494 return true;
495}
496
497static bool markerTypesFrom(const String& markerType, OptionSet<DocumentMarker::MarkerType>& result)
498{
499 DocumentMarker::MarkerType singularResult;
500
501 if (markerType.isEmpty() || equalLettersIgnoringASCIICase(markerType, "all"_s))
502 result = DocumentMarker::allMarkers();
503 else if (markerTypeFrom(markerType, singularResult))
504 result = singularResult;
505 else
506 return false;
507
508 return true;
509}
510
511static std::unique_ptr<PrintContext>& printContextForTesting()
512{
513 static NeverDestroyed<std::unique_ptr<PrintContext>> context;
514 return context;
515}
516
517Ref<Internals> Internals::create(Document& document)
518{
519 return adoptRef(*new Internals(document));
520}
521
522Internals::~Internals()
523{
524#if ENABLE(MEDIA_STREAM)
525 stopObservingRealtimeMediaSource();
526#endif
527#if ENABLE(MEDIA_SESSION)
528 if (m_artworkImagePromise)
529 m_artworkImagePromise->reject(Exception { InvalidStateError });
530#endif
531}
532
533void Internals::resetToConsistentState(Page& page)
534{
535 page.setPageScaleFactor(1, IntPoint(0, 0));
536 page.setPagination(Pagination());
537 page.setPaginationLineGridEnabled(false);
538
539 page.setDefersLoading(false);
540
541 page.mainFrame().setTextZoomFactor(1.0f);
542
543 page.setCompositingPolicyOverride(WebCore::CompositingPolicy::Normal);
544
545 FrameView* mainFrameView = page.mainFrame().view();
546 if (mainFrameView) {
547 page.setHeaderHeight(0);
548 page.setFooterHeight(0);
549 page.setTopContentInset(0);
550 mainFrameView->setUseFixedLayout(false);
551 mainFrameView->setFixedLayoutSize(IntSize());
552 mainFrameView->enableFixedWidthAutoSizeMode(false, { });
553#if USE(COORDINATED_GRAPHICS)
554 mainFrameView->setFixedVisibleContentRect(IntRect());
555#endif
556 if (auto* backing = mainFrameView->tiledBacking())
557 backing->setTileSizeUpdateDelayDisabledForTesting(false);
558 }
559
560 WTF::clearDefaultPortForProtocolMapForTesting();
561 overrideUserPreferredLanguages(Vector<String>());
562 WebCore::DeprecatedGlobalSettings::setUsesOverlayScrollbars(false);
563 if (!page.mainFrame().editor().isContinuousSpellCheckingEnabled())
564 page.mainFrame().editor().toggleContinuousSpellChecking();
565 if (page.mainFrame().editor().isOverwriteModeEnabled())
566 page.mainFrame().editor().toggleOverwriteModeEnabled();
567 page.mainFrame().loader().clearTestingOverrides();
568 page.applicationCacheStorage().setDefaultOriginQuota(ApplicationCacheStorage::noQuota());
569#if ENABLE(VIDEO)
570 page.group().ensureCaptionPreferences().setCaptionDisplayMode(CaptionUserPreferences::ForcedOnly);
571 page.group().ensureCaptionPreferences().setCaptionsStyleSheetOverride(emptyString());
572 PlatformMediaSessionManager::sharedManager().resetHaveEverRegisteredAsNowPlayingApplicationForTesting();
573 PlatformMediaSessionManager::sharedManager().resetRestrictions();
574 PlatformMediaSessionManager::sharedManager().resetSessionState();
575 PlatformMediaSessionManager::sharedManager().setWillIgnoreSystemInterruptions(true);
576 PlatformMediaSessionManager::sharedManager().applicationWillEnterForeground(false);
577#endif
578#if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
579 PlatformMediaSessionManager::sharedManager().setIsPlayingToAutomotiveHeadUnit(false);
580#endif
581#if ENABLE(ACCESSIBILITY)
582 AXObjectCache::setEnhancedUserInterfaceAccessibility(false);
583 AXObjectCache::disableAccessibility();
584#endif
585
586 MockPageOverlayClient::singleton().uninstallAllOverlays();
587
588#if ENABLE(CONTENT_FILTERING)
589 MockContentFilterSettings::reset();
590#endif
591
592#if ENABLE(WIRELESS_PLAYBACK_TARGET)
593 page.setMockMediaPlaybackTargetPickerEnabled(true);
594 page.setMockMediaPlaybackTargetPickerState(emptyString(), MediaPlaybackTargetContext::MockState::Unknown);
595#endif
596
597#if ENABLE(VIDEO)
598 MediaResourceLoader::recordResponsesForTesting();
599#endif
600
601 page.setShowAllPlugins(false);
602 page.setLowPowerModeEnabledOverrideForTesting(std::nullopt);
603 page.setOutsideViewportThrottlingEnabledForTesting(false);
604
605#if USE(QUICK_LOOK)
606 MockPreviewLoaderClient::singleton().setPassword(emptyString());
607 LegacyPreviewLoader::setClientForTesting(nullptr);
608#endif
609
610 printContextForTesting() = nullptr;
611
612#if USE(LIBWEBRTC)
613 auto& rtcProvider = page.libWebRTCProvider();
614 WebCore::useRealRTCPeerConnectionFactory(rtcProvider);
615 LibWebRTCProvider::setH264HardwareEncoderAllowed(true);
616 page.settings().setWebRTCEncryptionEnabled(true);
617 rtcProvider.disableNonLocalhostConnections();
618 rtcProvider.setH265Support(true);
619 rtcProvider.setVP9Support(true, true);
620 rtcProvider.clearFactory();
621#elif USE(GSTREAMER_WEBRTC)
622 page.settings().setWebRTCEncryptionEnabled(true);
623 page.settings().setPeerConnectionEnabled(true);
624#endif
625
626 page.setFullscreenAutoHideDuration(0_s);
627 page.setFullscreenInsets({ });
628 page.setFullscreenControlsHidden(false);
629
630 MediaEngineConfigurationFactory::disableMock();
631
632#if ENABLE(MEDIA_STREAM)
633 page.settings().setInterruptAudioOnPageVisibilityChangeEnabled(false);
634#endif
635
636#if ENABLE(MEDIA_RECORDER)
637 WebCore::MediaRecorder::setCustomPrivateRecorderCreator(nullptr);
638#endif
639
640 HTMLCanvasElement::setMaxPixelMemoryForTesting(std::nullopt);
641 HTMLCanvasElement::setMaxCanvasAreaForTesting(std::nullopt);
642 DOMWindow::overrideTransientActivationDurationForTesting(std::nullopt);
643
644#if PLATFORM(IOS)
645 WebCore::setContentSizeCategory(kCTFontContentSizeCategoryL);
646#endif
647
648#if ENABLE(MEDIA_SESSION) && USE(GLIB)
649 auto& sessionManager = reinterpret_cast<MediaSessionManagerGLib&>(PlatformMediaSessionManager::sharedManager());
650 sessionManager.setDBusNotificationsEnabled(false);
651#endif
652
653 TextPainter::setForceUseGlyphDisplayListForTesting(false);
654}
655
656Internals::Internals(Document& document)
657 : ContextDestructionObserver(&document)
658#if ENABLE(MEDIA_STREAM)
659 , m_orientationNotifier(0)
660#endif
661{
662#if ENABLE(WIRELESS_PLAYBACK_TARGET)
663 if (document.page())
664 document.page()->setMockMediaPlaybackTargetPickerEnabled(true);
665#endif
666
667#if ENABLE(VIDEO)
668 if (document.page())
669 m_testingModeToken = document.page()->group().ensureCaptionPreferences().createTestingModeToken().moveToUniquePtr();
670#endif
671
672 if (contextDocument() && contextDocument()->frame()) {
673 setAutomaticSpellingCorrectionEnabled(true);
674 setAutomaticQuoteSubstitutionEnabled(false);
675 setAutomaticDashSubstitutionEnabled(false);
676 setAutomaticLinkDetectionEnabled(false);
677 setAutomaticTextReplacementEnabled(true);
678 }
679
680 setConsoleMessageListener(nullptr);
681
682#if ENABLE(APPLE_PAY)
683 auto* frame = document.frame();
684 if (frame && frame->page() && frame->isMainFrame()) {
685 auto mockPaymentCoordinator = new MockPaymentCoordinator(*frame->page());
686 frame->page()->setPaymentCoordinator(makeUnique<PaymentCoordinator>(*mockPaymentCoordinator));
687 }
688#endif
689
690#if PLATFORM(COCOA) && ENABLE(WEB_AUDIO)
691 AudioDestinationCocoa::createOverride = nullptr;
692#endif
693
694#if PLATFORM(COCOA)
695 SystemBatteryStatusTestingOverrides::singleton().setHasAC(std::nullopt);
696 SystemBatteryStatusTestingOverrides::singleton().setHasBattery(std::nullopt);
697#endif
698
699#if ENABLE(VP9) && PLATFORM(COCOA)
700 VP9TestingOverrides::singleton().setHardwareDecoderDisabled(std::nullopt);
701 VP9TestingOverrides::singleton().setVP9ScreenSizeAndScale(std::nullopt);
702#endif
703}
704
705Document* Internals::contextDocument() const
706{
707 return downcast<Document>(scriptExecutionContext());
708}
709
710Frame* Internals::frame() const
711{
712 if (!contextDocument())
713 return nullptr;
714 return contextDocument()->frame();
715}
716
717InternalSettings* Internals::settings() const
718{
719 Document* document = contextDocument();
720 if (!document)
721 return nullptr;
722 Page* page = document->page();
723 if (!page)
724 return nullptr;
725 return InternalSettings::from(page);
726}
727
728unsigned Internals::inflightBeaconsCount() const
729{
730 auto* document = contextDocument();
731 if (!document)
732 return 0;
733
734 auto* window = document->domWindow();
735 if (!window)
736 return 0;
737
738 auto* navigator = window->optionalNavigator();
739 if (!navigator)
740 return 0;
741
742 return NavigatorBeacon::from(*navigator)->inflightBeaconsCount();
743}
744
745unsigned Internals::workerThreadCount() const
746{
747 return WorkerThread::workerThreadCount();
748}
749
750ExceptionOr<bool> Internals::areSVGAnimationsPaused() const
751{
752 auto* document = contextDocument();
753 if (!document)
754 return Exception { InvalidAccessError, "No context document"_s };
755
756 if (!document->svgExtensions())
757 return Exception { NotFoundError, "No SVG animations"_s };
758
759 return document->accessSVGExtensions().areAnimationsPaused();
760}
761
762ExceptionOr<double> Internals::svgAnimationsInterval(SVGSVGElement& element) const
763{
764 auto* document = contextDocument();
765 if (!document)
766 return 0;
767
768 if (!document->svgExtensions())
769 return 0;
770
771 if (document->accessSVGExtensions().areAnimationsPaused())
772 return 0;
773
774 return element.timeContainer().animationFrameDelay().value();
775}
776
777String Internals::address(Node& node)
778{
779 return makeString("0x", hex(reinterpret_cast<uintptr_t>(&node)));
780}
781
782bool Internals::nodeNeedsStyleRecalc(Node& node)
783{
784 return node.needsStyleRecalc();
785}
786
787static String styleValidityToToString(Style::Validity validity)
788{
789 switch (validity) {
790 case Style::Validity::Valid:
791 return "NoStyleChange"_s;
792 case Style::Validity::ElementInvalid:
793 return "InlineStyleChange"_s;
794 case Style::Validity::SubtreeInvalid:
795 return "FullStyleChange"_s;
796 case Style::Validity::SubtreeAndRenderersInvalid:
797 return "ReconstructRenderTree"_s;
798 }
799 ASSERT_NOT_REACHED();
800 return emptyString();
801}
802
803String Internals::styleChangeType(Node& node)
804{
805 node.document().styleScope().flushPendingUpdate();
806
807 return styleValidityToToString(node.styleValidity());
808}
809
810String Internals::description(JSC::JSValue value)
811{
812 return toString(value);
813}
814
815void Internals::log(const String& value)
816{
817 WTFLogAlways("%s", value.utf8().data());
818}
819
820bool Internals::isPreloaded(const String& url)
821{
822 Document* document = contextDocument();
823 return document->cachedResourceLoader().isPreloaded(url);
824}
825
826bool Internals::isLoadingFromMemoryCache(const String& url)
827{
828 if (!contextDocument() || !contextDocument()->page())
829 return false;
830
831 ResourceRequest request(contextDocument()->completeURL(url));
832 request.setDomainForCachePartition(contextDocument()->domainForCachePartition());
833
834 CachedResource* resource = MemoryCache::singleton().resourceForRequest(request, contextDocument()->page()->sessionID());
835 return resource && resource->status() == CachedResource::Cached;
836}
837
838static String responseSourceToString(const ResourceResponse& response)
839{
840 if (response.isNull())
841 return "Null response"_s;
842 switch (response.source()) {
843 case ResourceResponse::Source::Unknown:
844 return "Unknown"_s;
845 case ResourceResponse::Source::Network:
846 return "Network"_s;
847 case ResourceResponse::Source::ServiceWorker:
848 return "Service worker"_s;
849 case ResourceResponse::Source::DiskCache:
850 return "Disk cache"_s;
851 case ResourceResponse::Source::DiskCacheAfterValidation:
852 return "Disk cache after validation"_s;
853 case ResourceResponse::Source::MemoryCache:
854 return "Memory cache"_s;
855 case ResourceResponse::Source::MemoryCacheAfterValidation:
856 return "Memory cache after validation"_s;
857 case ResourceResponse::Source::ApplicationCache:
858 return "Application cache"_s;
859 case ResourceResponse::Source::DOMCache:
860 return "DOM cache"_s;
861 case ResourceResponse::Source::InspectorOverride:
862 return "Inspector override"_s;
863 }
864 ASSERT_NOT_REACHED();
865 return "Error"_s;
866}
867
868String Internals::xhrResponseSource(XMLHttpRequest& request)
869{
870 return responseSourceToString(request.resourceResponse());
871}
872
873String Internals::fetchResponseSource(FetchResponse& response)
874{
875 return responseSourceToString(response.resourceResponse());
876}
877
878String Internals::blobInternalURL(const Blob& blob)
879{
880 return blob.url().string();
881}
882
883void Internals::isBlobInternalURLRegistered(const String& url, DOMPromiseDeferred<IDLBoolean>&& promise)
884{
885 promise.resolve(!!ThreadableBlobRegistry::blobSize(URL { url }));
886}
887
888bool Internals::isSharingStyleSheetContents(HTMLLinkElement& a, HTMLLinkElement& b)
889{
890 if (!a.sheet() || !b.sheet())
891 return false;
892 return &a.sheet()->contents() == &b.sheet()->contents();
893}
894
895bool Internals::isStyleSheetLoadingSubresources(HTMLLinkElement& link)
896{
897 return link.sheet() && link.sheet()->contents().isLoadingSubresources();
898}
899
900static ResourceRequestCachePolicy toResourceRequestCachePolicy(Internals::CachePolicy policy)
901{
902 switch (policy) {
903 case Internals::CachePolicy::UseProtocolCachePolicy:
904 return ResourceRequestCachePolicy::UseProtocolCachePolicy;
905 case Internals::CachePolicy::ReloadIgnoringCacheData:
906 return ResourceRequestCachePolicy::ReloadIgnoringCacheData;
907 case Internals::CachePolicy::ReturnCacheDataElseLoad:
908 return ResourceRequestCachePolicy::ReturnCacheDataElseLoad;
909 case Internals::CachePolicy::ReturnCacheDataDontLoad:
910 return ResourceRequestCachePolicy::ReturnCacheDataDontLoad;
911 }
912 ASSERT_NOT_REACHED();
913 return ResourceRequestCachePolicy::UseProtocolCachePolicy;
914}
915
916void Internals::setOverrideCachePolicy(CachePolicy policy)
917{
918 frame()->loader().setOverrideCachePolicyForTesting(toResourceRequestCachePolicy(policy));
919}
920
921ExceptionOr<void> Internals::setCanShowModalDialogOverride(bool allow)
922{
923 if (!contextDocument() || !contextDocument()->domWindow())
924 return Exception { InvalidAccessError };
925
926 contextDocument()->domWindow()->setCanShowModalDialogOverride(allow);
927 return { };
928}
929
930static ResourceLoadPriority toResourceLoadPriority(Internals::ResourceLoadPriority priority)
931{
932 switch (priority) {
933 case Internals::ResourceLoadPriority::ResourceLoadPriorityVeryLow:
934 return ResourceLoadPriority::VeryLow;
935 case Internals::ResourceLoadPriority::ResourceLoadPriorityLow:
936 return ResourceLoadPriority::Low;
937 case Internals::ResourceLoadPriority::ResourceLoadPriorityMedium:
938 return ResourceLoadPriority::Medium;
939 case Internals::ResourceLoadPriority::ResourceLoadPriorityHigh:
940 return ResourceLoadPriority::High;
941 case Internals::ResourceLoadPriority::ResourceLoadPriorityVeryHigh:
942 return ResourceLoadPriority::VeryHigh;
943 }
944 ASSERT_NOT_REACHED();
945 return ResourceLoadPriority::Low;
946}
947
948void Internals::setOverrideResourceLoadPriority(ResourceLoadPriority priority)
949{
950 frame()->loader().setOverrideResourceLoadPriorityForTesting(toResourceLoadPriority(priority));
951}
952
953void Internals::setStrictRawResourceValidationPolicyDisabled(bool disabled)
954{
955 frame()->loader().setStrictRawResourceValidationPolicyDisabledForTesting(disabled);
956}
957
958bool Internals::isFetchObjectContextStopped(const FetchObject& object)
959{
960 return switchOn(object, [](const RefPtr<FetchRequest>& request) {
961 return request->isContextStopped();
962 }, [](auto& response) {
963 return response->isContextStopped();
964 });
965}
966
967void Internals::clearMemoryCache()
968{
969 MemoryCache::singleton().evictResources();
970 CrossOriginPreflightResultCache::singleton().clear();
971}
972
973void Internals::pruneMemoryCacheToSize(unsigned size)
974{
975 MemoryCache::singleton().pruneDeadResourcesToSize(size);
976 MemoryCache::singleton().pruneLiveResourcesToSize(size, true);
977}
978
979void Internals::destroyDecodedDataForAllImages()
980{
981 MemoryCache::singleton().destroyDecodedDataForAllImages();
982}
983
984unsigned Internals::memoryCacheSize() const
985{
986 return MemoryCache::singleton().size();
987}
988
989static Image* imageFromImageElement(HTMLImageElement& element)
990{
991 auto* cachedImage = element.cachedImage();
992 return cachedImage ? cachedImage->image() : nullptr;
993}
994
995static BitmapImage* bitmapImageFromImageElement(HTMLImageElement& element)
996{
997 return dynamicDowncast<BitmapImage>(imageFromImageElement(element));
998}
999
1000#if USE(CG)
1001static PDFDocumentImage* pdfDocumentImageFromImageElement(HTMLImageElement& element)
1002{
1003 return dynamicDowncast<PDFDocumentImage>(imageFromImageElement(element));
1004}
1005#endif
1006
1007unsigned Internals::imageFrameIndex(HTMLImageElement& element)
1008{
1009 auto* bitmapImage = bitmapImageFromImageElement(element);
1010 return bitmapImage ? bitmapImage->currentFrame() : 0;
1011}
1012
1013unsigned Internals::imageFrameCount(HTMLImageElement& element)
1014{
1015 auto* bitmapImage = bitmapImageFromImageElement(element);
1016 return bitmapImage ? bitmapImage->frameCount() : 0;
1017}
1018
1019float Internals::imageFrameDurationAtIndex(HTMLImageElement& element, unsigned index)
1020{
1021 auto* bitmapImage = bitmapImageFromImageElement(element);
1022 return bitmapImage ? bitmapImage->frameDurationAtIndex(index).value() : 0;
1023}
1024
1025void Internals::setImageFrameDecodingDuration(HTMLImageElement& element, float duration)
1026{
1027 if (auto* bitmapImage = bitmapImageFromImageElement(element))
1028 bitmapImage->setFrameDecodingDurationForTesting(Seconds { duration });
1029}
1030
1031void Internals::resetImageAnimation(HTMLImageElement& element)
1032{
1033 if (auto* image = imageFromImageElement(element))
1034 image->resetAnimation();
1035}
1036
1037bool Internals::isImageAnimating(HTMLImageElement& element)
1038{
1039 auto* image = imageFromImageElement(element);
1040 return image && (image->isAnimating() || image->animationPending());
1041}
1042
1043unsigned Internals::imagePendingDecodePromisesCountForTesting(HTMLImageElement& element)
1044{
1045 return element.pendingDecodePromisesCountForTesting();
1046}
1047
1048void Internals::setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement& element, bool enabled)
1049{
1050 if (auto* bitmapImage = bitmapImageFromImageElement(element))
1051 bitmapImage->setClearDecoderAfterAsyncFrameRequestForTesting(enabled);
1052}
1053
1054unsigned Internals::imageDecodeCount(HTMLImageElement& element)
1055{
1056 auto* bitmapImage = bitmapImageFromImageElement(element);
1057 return bitmapImage ? bitmapImage->decodeCountForTesting() : 0;
1058}
1059
1060unsigned Internals::pdfDocumentCachingCount(HTMLImageElement& element)
1061{
1062#if USE(CG)
1063 auto* pdfDocumentImage = pdfDocumentImageFromImageElement(element);
1064 return pdfDocumentImage ? pdfDocumentImage->cachingCountForTesting() : 0;
1065#else
1066 UNUSED_PARAM(element);
1067 return 0;
1068#endif
1069}
1070
1071unsigned Internals::remoteImagesCountForTesting() const
1072{
1073 Document* document = contextDocument();
1074 if (!document || !document->page())
1075 return 0;
1076
1077 return document->page()->chrome().client().remoteImagesCountForTesting();
1078}
1079
1080void Internals::setLargeImageAsyncDecodingEnabledForTesting(HTMLImageElement& element, bool enabled)
1081{
1082 if (auto* bitmapImage = bitmapImageFromImageElement(element))
1083 bitmapImage->setLargeImageAsyncDecodingEnabledForTesting(enabled);
1084}
1085
1086void Internals::setForceUpdateImageDataEnabledForTesting(HTMLImageElement& element, bool enabled)
1087{
1088 if (auto* cachedImage = element.cachedImage())
1089 cachedImage->setForceUpdateImageDataEnabledForTesting(enabled);
1090}
1091
1092void Internals::setGridMaxTracksLimit(unsigned maxTrackLimit)
1093{
1094 GridPosition::setMaxPositionForTesting(maxTrackLimit);
1095}
1096
1097void Internals::clearBackForwardCache()
1098{
1099 BackForwardCache::singleton().pruneToSizeNow(0, PruningReason::None);
1100}
1101
1102unsigned Internals::backForwardCacheSize() const
1103{
1104 return BackForwardCache::singleton().pageCount();
1105}
1106
1107void Internals::preventDocumentFromEnteringBackForwardCache()
1108{
1109 if (auto* document = contextDocument())
1110 document->preventEnteringBackForwardCacheForTesting();
1111}
1112
1113void Internals::disableTileSizeUpdateDelay()
1114{
1115 Document* document = contextDocument();
1116 if (!document || !document->frame())
1117 return;
1118
1119 auto* view = document->frame()->view();
1120 if (!view)
1121 return;
1122
1123 if (auto* backing = view->tiledBacking())
1124 backing->setTileSizeUpdateDelayDisabledForTesting(true);
1125}
1126
1127void Internals::setSpeculativeTilingDelayDisabledForTesting(bool disabled)
1128{
1129 Document* document = contextDocument();
1130 if (!document || !document->frame())
1131 return;
1132
1133 if (auto* frameView = document->frame()->view())
1134 frameView->setSpeculativeTilingDelayDisabledForTesting(disabled);
1135}
1136
1137
1138Node* Internals::treeScopeRootNode(Node& node)
1139{
1140 return &node.treeScope().rootNode();
1141}
1142
1143Node* Internals::parentTreeScope(Node& node)
1144{
1145 const TreeScope* parentTreeScope = node.treeScope().parentTreeScope();
1146 return parentTreeScope ? &parentTreeScope->rootNode() : nullptr;
1147}
1148
1149ExceptionOr<unsigned> Internals::lastSpatialNavigationCandidateCount() const
1150{
1151 if (!contextDocument() || !contextDocument()->page())
1152 return Exception { InvalidAccessError };
1153
1154 return contextDocument()->page()->lastSpatialNavigationCandidateCount();
1155}
1156
1157bool Internals::animationWithIdExists(const String& id) const
1158{
1159 for (auto* animation : WebAnimation::instances()) {
1160 if (animation->id() == id)
1161 return true;
1162 }
1163 return false;
1164}
1165
1166unsigned Internals::numberOfActiveAnimations() const
1167{
1168 return frame()->document()->timeline().numberOfActiveAnimationsForTesting();
1169}
1170
1171ExceptionOr<bool> Internals::animationsAreSuspended() const
1172{
1173 Document* document = contextDocument();
1174 if (!document || !document->frame())
1175 return Exception { InvalidAccessError };
1176
1177 return document->ensureTimelinesController().animationsAreSuspended();
1178}
1179
1180double Internals::animationsInterval() const
1181{
1182 Document* document = contextDocument();
1183 if (!document)
1184 return INFINITY;
1185
1186 if (auto timeline = document->existingTimeline())
1187 return timeline->animationInterval().seconds();
1188 return INFINITY;
1189}
1190
1191ExceptionOr<void> Internals::suspendAnimations() const
1192{
1193 Document* document = contextDocument();
1194 if (!document || !document->frame())
1195 return Exception { InvalidAccessError };
1196
1197 document->ensureTimelinesController().suspendAnimations();
1198 for (Frame* frame = document->frame(); frame; frame = frame->tree().traverseNext()) {
1199 if (Document* document = frame->document())
1200 document->ensureTimelinesController().suspendAnimations();
1201 }
1202
1203 return { };
1204}
1205
1206ExceptionOr<void> Internals::resumeAnimations() const
1207{
1208 Document* document = contextDocument();
1209 if (!document || !document->frame())
1210 return Exception { InvalidAccessError };
1211
1212 document->ensureTimelinesController().resumeAnimations();
1213 for (Frame* frame = document->frame(); frame; frame = frame->tree().traverseNext()) {
1214 if (Document* document = frame->document())
1215 document->ensureTimelinesController().resumeAnimations();
1216 }
1217
1218 return { };
1219}
1220
1221Vector<Internals::AcceleratedAnimation> Internals::acceleratedAnimationsForElement(Element& element)
1222{
1223 Vector<Internals::AcceleratedAnimation> animations;
1224 for (const auto& animationAsPair : element.document().timeline().acceleratedAnimationsForElement(element))
1225 animations.append({ animationAsPair.first, animationAsPair.second });
1226 return animations;
1227}
1228
1229unsigned Internals::numberOfAnimationTimelineInvalidations() const
1230{
1231 return frame()->document()->timeline().numberOfAnimationTimelineInvalidationsForTesting();
1232}
1233
1234double Internals::timeToNextAnimationTick(WebAnimation& animation) const
1235{
1236 return secondsToWebAnimationsAPITime(animation.timeToNextTick());
1237}
1238
1239ExceptionOr<RefPtr<Element>> Internals::pseudoElement(Element& element, const String& pseudoId)
1240{
1241 if (pseudoId != "before"_s && pseudoId != "after"_s)
1242 return Exception { InvalidAccessError };
1243
1244 return pseudoId == "before"_s ? element.beforePseudoElement() : element.afterPseudoElement();
1245}
1246
1247ExceptionOr<String> Internals::elementRenderTreeAsText(Element& element)
1248{
1249 element.document().updateStyleIfNeeded();
1250
1251 String representation = externalRepresentation(&element);
1252 if (representation.isEmpty())
1253 return Exception { InvalidAccessError };
1254
1255 return representation;
1256}
1257
1258bool Internals::hasPausedImageAnimations(Element& element)
1259{
1260 return element.renderer() && element.renderer()->hasPausedImageAnimations();
1261}
1262
1263bool Internals::isPaintingFrequently(Element& element)
1264{
1265 return element.renderer() && element.renderer()->enclosingLayer() && element.renderer()->enclosingLayer()->paintingFrequently();
1266}
1267
1268void Internals::incrementFrequentPaintCounter(Element& element)
1269{
1270 if (element.renderer() && element.renderer()->enclosingLayer())
1271 element.renderer()->enclosingLayer()->simulateFrequentPaint();
1272}
1273
1274Ref<CSSComputedStyleDeclaration> Internals::computedStyleIncludingVisitedInfo(Element& element) const
1275{
1276 bool allowVisitedStyle = true;
1277 return CSSComputedStyleDeclaration::create(element, allowVisitedStyle);
1278}
1279
1280Node* Internals::ensureUserAgentShadowRoot(Element& host)
1281{
1282 return &host.ensureUserAgentShadowRoot();
1283}
1284
1285Node* Internals::shadowRoot(Element& host)
1286{
1287 if (host.document().hasElementWithPendingUserAgentShadowTreeUpdate(host)) {
1288 host.updateUserAgentShadowTree();
1289 host.document().removeElementWithPendingUserAgentShadowTreeUpdate(host);
1290 }
1291 return host.shadowRoot();
1292}
1293
1294ExceptionOr<String> Internals::shadowRootType(const Node& root) const
1295{
1296 if (!is<ShadowRoot>(root))
1297 return Exception { InvalidAccessError };
1298
1299 switch (downcast<ShadowRoot>(root).mode()) {
1300 case ShadowRootMode::UserAgent:
1301 return "UserAgentShadowRoot"_str;
1302 case ShadowRootMode::Closed:
1303 return "ClosedShadowRoot"_str;
1304 case ShadowRootMode::Open:
1305 return "OpenShadowRoot"_str;
1306 default:
1307 ASSERT_NOT_REACHED();
1308 return "Unknown"_str;
1309 }
1310}
1311
1312const AtomString& Internals::shadowPseudoId(Element& element)
1313{
1314 return element.shadowPseudoId();
1315}
1316
1317void Internals::setShadowPseudoId(Element& element, const AtomString& id)
1318{
1319 return element.setPseudo(id);
1320}
1321
1322ExceptionOr<bool> Internals::isTimerThrottled(int timeoutId)
1323{
1324 auto* timer = scriptExecutionContext()->findTimeout(timeoutId);
1325 if (!timer)
1326 return Exception { NotFoundError };
1327
1328 if (timer->intervalClampedToMinimum() > timer->m_originalInterval)
1329 return true;
1330
1331 return !!timer->alignedFireTime(MonotonicTime { });
1332}
1333
1334String Internals::requestAnimationFrameThrottlingReasons() const
1335{
1336 auto* scriptedAnimationController = contextDocument()->scriptedAnimationController();
1337 if (!scriptedAnimationController)
1338 return String();
1339
1340 TextStream ts;
1341 ts << scriptedAnimationController->throttlingReasons();
1342 return ts.release();
1343}
1344
1345double Internals::requestAnimationFrameInterval() const
1346{
1347 auto* scriptedAnimationController = contextDocument()->scriptedAnimationController();
1348 if (!scriptedAnimationController)
1349 return INFINITY;
1350 return scriptedAnimationController->interval().value();
1351}
1352
1353bool Internals::scriptedAnimationsAreSuspended() const
1354{
1355 Document* document = contextDocument();
1356 if (!document || !document->page())
1357 return true;
1358
1359 return document->page()->scriptedAnimationsSuspended();
1360}
1361
1362bool Internals::areTimersThrottled() const
1363{
1364 return contextDocument()->isTimerThrottlingEnabled();
1365}
1366
1367void Internals::setEventThrottlingBehaviorOverride(std::optional<EventThrottlingBehavior> value)
1368{
1369 Document* document = contextDocument();
1370 if (!document || !document->page())
1371 return;
1372
1373 if (!value) {
1374 document->page()->setEventThrottlingBehaviorOverride(std::nullopt);
1375 return;
1376 }
1377
1378 switch (value.value()) {
1379 case Internals::EventThrottlingBehavior::Responsive:
1380 document->page()->setEventThrottlingBehaviorOverride(WebCore::EventThrottlingBehavior::Responsive);
1381 break;
1382 case Internals::EventThrottlingBehavior::Unresponsive:
1383 document->page()->setEventThrottlingBehaviorOverride(WebCore::EventThrottlingBehavior::Unresponsive);
1384 break;
1385 }
1386}
1387
1388std::optional<Internals::EventThrottlingBehavior> Internals::eventThrottlingBehaviorOverride() const
1389{
1390 Document* document = contextDocument();
1391 if (!document || !document->page())
1392 return std::nullopt;
1393
1394 auto behavior = document->page()->eventThrottlingBehaviorOverride();
1395 if (!behavior)
1396 return std::nullopt;
1397
1398 switch (behavior.value()) {
1399 case WebCore::EventThrottlingBehavior::Responsive:
1400 return Internals::EventThrottlingBehavior::Responsive;
1401 case WebCore::EventThrottlingBehavior::Unresponsive:
1402 return Internals::EventThrottlingBehavior::Unresponsive;
1403 }
1404
1405 return std::nullopt;
1406}
1407
1408String Internals::visiblePlaceholder(Element& element)
1409{
1410 if (is<HTMLTextFormControlElement>(element)) {
1411 const HTMLTextFormControlElement& textFormControlElement = downcast<HTMLTextFormControlElement>(element);
1412 if (!textFormControlElement.isPlaceholderVisible())
1413 return String();
1414 if (HTMLElement* placeholderElement = textFormControlElement.placeholderElement())
1415 return placeholderElement->textContent();
1416 }
1417
1418 return String();
1419}
1420
1421void Internals::setCanShowPlaceholder(Element& element, bool canShowPlaceholder)
1422{
1423 if (is<HTMLTextFormControlElement>(element))
1424 downcast<HTMLTextFormControlElement>(element).setCanShowPlaceholder(canShowPlaceholder);
1425}
1426
1427Element* Internals::insertTextPlaceholder(int width, int height)
1428{
1429 return frame()->editor().insertTextPlaceholder(IntSize { width, height }).get();
1430}
1431
1432void Internals::removeTextPlaceholder(Element& element)
1433{
1434 if (is<TextPlaceholderElement>(element))
1435 frame()->editor().removeTextPlaceholder(downcast<TextPlaceholderElement>(element));
1436}
1437
1438void Internals::selectColorInColorChooser(HTMLInputElement& element, const String& colorValue)
1439{
1440 element.selectColor(colorValue);
1441}
1442
1443ExceptionOr<Vector<AtomString>> Internals::formControlStateOfPreviousHistoryItem()
1444{
1445 HistoryItem* mainItem = frame()->loader().history().previousItem();
1446 if (!mainItem)
1447 return Exception { InvalidAccessError };
1448 auto uniqueName = frame()->tree().uniqueName();
1449 if (mainItem->target() != uniqueName && !mainItem->childItemWithTarget(uniqueName))
1450 return Exception { InvalidAccessError };
1451 return Vector<AtomString> { mainItem->target() == uniqueName ? mainItem->documentState() : mainItem->childItemWithTarget(uniqueName)->documentState() };
1452}
1453
1454ExceptionOr<void> Internals::setFormControlStateOfPreviousHistoryItem(const Vector<AtomString>& state)
1455{
1456 HistoryItem* mainItem = frame()->loader().history().previousItem();
1457 if (!mainItem)
1458 return Exception { InvalidAccessError };
1459 auto uniqueName = frame()->tree().uniqueName();
1460 if (mainItem->target() == uniqueName)
1461 mainItem->setDocumentState(state);
1462 else if (HistoryItem* subItem = mainItem->childItemWithTarget(uniqueName))
1463 subItem->setDocumentState(state);
1464 else
1465 return Exception { InvalidAccessError };
1466 return { };
1467}
1468
1469#if ENABLE(SPEECH_SYNTHESIS)
1470
1471void Internals::enableMockSpeechSynthesizer()
1472{
1473 Document* document = contextDocument();
1474 if (!document || !document->domWindow())
1475 return;
1476 SpeechSynthesis* synthesis = DOMWindowSpeechSynthesis::speechSynthesis(*document->domWindow());
1477 if (!synthesis)
1478 return;
1479
1480 synthesis->setPlatformSynthesizer(makeUnique<PlatformSpeechSynthesizerMock>(synthesis));
1481}
1482
1483#endif
1484
1485#if ENABLE(WEB_RTC)
1486
1487void Internals::emulateRTCPeerConnectionPlatformEvent(RTCPeerConnection& connection, const String& action)
1488{
1489 if (!LibWebRTCProvider::webRTCAvailable())
1490 return;
1491
1492 connection.emulatePlatformEvent(action);
1493}
1494
1495void Internals::useMockRTCPeerConnectionFactory(const String& testCase)
1496{
1497 if (!LibWebRTCProvider::webRTCAvailable())
1498 return;
1499
1500#if USE(LIBWEBRTC)
1501 Document* document = contextDocument();
1502 LibWebRTCProvider* provider = (document && document->page()) ? &document->page()->libWebRTCProvider() : nullptr;
1503 WebCore::useMockRTCPeerConnectionFactory(provider, testCase);
1504#else
1505 UNUSED_PARAM(testCase);
1506#endif
1507}
1508
1509void Internals::setICECandidateFiltering(bool enabled)
1510{
1511 auto* page = contextDocument()->page();
1512 if (!page)
1513 return;
1514
1515 auto& rtcController = page->rtcController();
1516 if (enabled)
1517 rtcController.enableICECandidateFiltering();
1518 else
1519 rtcController.disableICECandidateFilteringForAllOrigins();
1520}
1521
1522void Internals::setEnumeratingAllNetworkInterfacesEnabled(bool enabled)
1523{
1524#if USE(LIBWEBRTC)
1525 Document* document = contextDocument();
1526 auto* page = document->page();
1527 if (!page)
1528 return;
1529 auto& rtcProvider = page->libWebRTCProvider();
1530 if (enabled)
1531 rtcProvider.enableEnumeratingAllNetworkInterfaces();
1532 else
1533 rtcProvider.disableEnumeratingAllNetworkInterfaces();
1534#else
1535 UNUSED_PARAM(enabled);
1536#endif
1537}
1538
1539void Internals::stopPeerConnection(RTCPeerConnection& connection)
1540{
1541 ActiveDOMObject& object = connection;
1542 object.stop();
1543}
1544
1545void Internals::clearPeerConnectionFactory()
1546{
1547#if USE(LIBWEBRTC)
1548 if (auto* page = contextDocument()->page())
1549 page->libWebRTCProvider().clearFactory();
1550#endif
1551}
1552
1553void Internals::applyRotationForOutgoingVideoSources(RTCPeerConnection& connection)
1554{
1555 connection.applyRotationForOutgoingVideoSources();
1556}
1557
1558void Internals::setWebRTCH265Support(bool value)
1559{
1560#if USE(LIBWEBRTC)
1561 if (auto* page = contextDocument()->page()) {
1562 page->libWebRTCProvider().setH265Support(value);
1563 page->libWebRTCProvider().clearFactory();
1564 }
1565#else
1566 UNUSED_PARAM(value);
1567#endif
1568}
1569
1570void Internals::setWebRTCVP9Support(bool supportVP9Profile0, bool supportVP9Profile2)
1571{
1572#if USE(LIBWEBRTC)
1573 if (auto* page = contextDocument()->page()) {
1574 page->libWebRTCProvider().setVP9Support(supportVP9Profile0, supportVP9Profile2);
1575 page->libWebRTCProvider().clearFactory();
1576 }
1577#else
1578 UNUSED_PARAM(supportVP9Profile0);
1579 UNUSED_PARAM(supportVP9Profile2);
1580#endif
1581}
1582
1583void Internals::setWebRTCVP9VTBSupport(bool value)
1584{
1585#if USE(LIBWEBRTC)
1586 if (auto* page = contextDocument()->page()) {
1587 page->libWebRTCProvider().setVP9VTBSupport(value);
1588 page->libWebRTCProvider().clearFactory();
1589 }
1590#else
1591 UNUSED_PARAM(value);
1592#endif
1593}
1594
1595bool Internals::isSupportingVP9VTB() const
1596{
1597#if USE(LIBWEBRTC)
1598 if (auto* page = contextDocument()->page())
1599 return page->libWebRTCProvider().isSupportingVP9VTB();
1600#endif
1601 return false;
1602}
1603
1604void Internals::isVP9VTBDeccoderUsed(RTCPeerConnection& connection, DOMPromiseDeferred<IDLBoolean>&& promise)
1605{
1606 connection.gatherDecoderImplementationName([promise = WTFMove(promise)](auto&& name) mutable {
1607 promise.resolve(name.contains("VideoToolBox"_s));
1608 });
1609}
1610
1611void Internals::setSFrameCounter(RTCRtpSFrameTransform& transform, const String& counter)
1612{
1613 if (auto value = parseInteger<uint64_t>(counter))
1614 transform.setCounterForTesting(*value);
1615}
1616
1617uint64_t Internals::sframeCounter(const RTCRtpSFrameTransform& transform)
1618{
1619 return transform.counterForTesting();
1620}
1621
1622uint64_t Internals::sframeKeyId(const RTCRtpSFrameTransform& transform)
1623{
1624 return transform.keyIdForTesting();
1625}
1626
1627void Internals::setEnableWebRTCEncryption(bool value)
1628{
1629#if USE(LIBWEBRTC)
1630 if (auto* page = contextDocument()->page())
1631 page->settings().setWebRTCEncryptionEnabled(value);
1632#else
1633 UNUSED_PARAM(value);
1634#endif
1635}
1636
1637void Internals::setUseDTLS10(bool useDTLS10)
1638{
1639#if USE(LIBWEBRTC)
1640 auto* document = contextDocument();
1641 if (!document || !document->page())
1642 return;
1643 document->page()->libWebRTCProvider().setUseDTLS10(useDTLS10);
1644#else
1645 UNUSED_PARAM(useDTLS10);
1646#endif
1647}
1648
1649#endif
1650
1651#if ENABLE(MEDIA_STREAM)
1652void Internals::setShouldInterruptAudioOnPageVisibilityChange(bool shouldInterrupt)
1653{
1654 Document* document = contextDocument();
1655 if (auto* page = document->page())
1656 page->settings().setInterruptAudioOnPageVisibilityChangeEnabled(shouldInterrupt);
1657}
1658#endif // ENABLE(MEDIA_STREAM)
1659
1660#if ENABLE(MEDIA_RECORDER)
1661static ExceptionOr<std::unique_ptr<MediaRecorderPrivate>> createRecorderMockSource(MediaStreamPrivate& stream, const MediaRecorderPrivateOptions&)
1662{
1663 return std::unique_ptr<MediaRecorderPrivate>(new MediaRecorderPrivateMock(stream));
1664}
1665
1666void Internals::setCustomPrivateRecorderCreator()
1667{
1668 WebCore::MediaRecorder::setCustomPrivateRecorderCreator(createRecorderMockSource);
1669}
1670#endif // ENABLE(MEDIA_RECORDER)
1671
1672ExceptionOr<Ref<DOMRect>> Internals::absoluteLineRectFromPoint(int x, int y)
1673{
1674 if (!contextDocument() || !contextDocument()->page())
1675 return Exception { InvalidAccessError };
1676
1677 auto& document = *contextDocument();
1678 if (!document.frame() || !document.view())
1679 return Exception { InvalidAccessError };
1680
1681 auto& frame = *document.frame();
1682 auto& view = *document.view();
1683 document.updateLayoutIgnorePendingStylesheets();
1684
1685 auto position = frame.visiblePositionForPoint(view.rootViewToContents(IntPoint { x, y }));
1686 return DOMRect::create(position.absoluteSelectionBoundsForLine());
1687}
1688
1689ExceptionOr<Ref<DOMRect>> Internals::absoluteCaretBounds()
1690{
1691 Document* document = contextDocument();
1692 if (!document || !document->frame())
1693 return Exception { InvalidAccessError };
1694
1695 return DOMRect::create(document->frame()->selection().absoluteCaretBounds());
1696}
1697
1698ExceptionOr<bool> Internals::isCaretBlinkingSuspended()
1699{
1700 Document* document = contextDocument();
1701 if (!document || !document->frame())
1702 return Exception { InvalidAccessError };
1703
1704 return document->frame()->selection().isCaretBlinkingSuspended();
1705}
1706
1707Ref<DOMRect> Internals::boundingBox(Element& element)
1708{
1709 element.document().updateLayoutIgnorePendingStylesheets();
1710 auto renderer = element.renderer();
1711 if (!renderer)
1712 return DOMRect::create();
1713 return DOMRect::create(renderer->absoluteBoundingBoxRectIgnoringTransforms());
1714}
1715
1716ExceptionOr<unsigned> Internals::inspectorGridOverlayCount()
1717{
1718 Document* document = contextDocument();
1719 if (!document || !document->page())
1720 return Exception { InvalidAccessError };
1721
1722 return document->page()->inspectorController().gridOverlayCount();
1723}
1724
1725ExceptionOr<unsigned> Internals::inspectorFlexOverlayCount()
1726{
1727 Document* document = contextDocument();
1728 if (!document || !document->page())
1729 return Exception { InvalidAccessError };
1730
1731 return document->page()->inspectorController().flexOverlayCount();
1732}
1733
1734ExceptionOr<Ref<DOMRectList>> Internals::inspectorHighlightRects()
1735{
1736 Document* document = contextDocument();
1737 if (!document || !document->page())
1738 return Exception { InvalidAccessError };
1739
1740 InspectorOverlay::Highlight highlight;
1741 document->page()->inspectorController().getHighlight(highlight, InspectorOverlay::CoordinateSystem::View);
1742 return DOMRectList::create(highlight.quads);
1743}
1744
1745ExceptionOr<unsigned> Internals::inspectorPaintRectCount()
1746{
1747 auto document = contextDocument();
1748 if (!document || !document->page())
1749 return Exception { InvalidAccessError };
1750
1751 return document->page()->inspectorController().paintRectCount();
1752}
1753
1754ExceptionOr<unsigned> Internals::markerCountForNode(Node& node, const String& markerType)
1755{
1756 OptionSet<DocumentMarker::MarkerType> markerTypes;
1757 if (!markerTypesFrom(markerType, markerTypes))
1758 return Exception { SyntaxError };
1759
1760 node.document().editor().updateEditorUINowIfScheduled();
1761 return node.document().markers().markersFor(node, markerTypes).size();
1762}
1763
1764ExceptionOr<RenderedDocumentMarker*> Internals::markerAt(Node& node, const String& markerType, unsigned index)
1765{
1766 node.document().updateLayoutIgnorePendingStylesheets();
1767
1768 OptionSet<DocumentMarker::MarkerType> markerTypes;
1769 if (!markerTypesFrom(markerType, markerTypes))
1770 return Exception { SyntaxError };
1771
1772 node.document().editor().updateEditorUINowIfScheduled();
1773
1774 Vector<RenderedDocumentMarker*> markers = node.document().markers().markersFor(node, markerTypes);
1775 if (markers.size() <= index)
1776 return nullptr;
1777 return markers[index];
1778}
1779
1780ExceptionOr<RefPtr<Range>> Internals::markerRangeForNode(Node& node, const String& markerType, unsigned index)
1781{
1782 auto result = markerAt(node, markerType, index);
1783 if (result.hasException())
1784 return result.releaseException();
1785 auto marker = result.releaseReturnValue();
1786 if (!marker)
1787 return nullptr;
1788 return { createLiveRange(makeSimpleRange(node, *marker)) };
1789}
1790
1791ExceptionOr<String> Internals::markerDescriptionForNode(Node& node, const String& markerType, unsigned index)
1792{
1793 auto result = markerAt(node, markerType, index);
1794 if (result.hasException())
1795 return result.releaseException();
1796 auto marker = result.releaseReturnValue();
1797 if (!marker)
1798 return String();
1799 return String { marker->description() };
1800}
1801
1802ExceptionOr<String> Internals::dumpMarkerRects(const String& markerTypeString)
1803{
1804 DocumentMarker::MarkerType markerType;
1805 if (!markerTypeFrom(markerTypeString, markerType))
1806 return Exception { SyntaxError };
1807
1808 contextDocument()->markers().updateRectsForInvalidatedMarkersOfType(markerType);
1809 auto rects = contextDocument()->markers().renderedRectsForMarkers(markerType);
1810
1811 // 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.
1812 StringBuilder rectString;
1813 rectString.append("marker rects: ");
1814 for (const auto& rect : rects)
1815 rectString.append('(', rect.x(), ", ", rect.y(), ", ", FormattedNumber::fixedPrecision(rect.width()), ", ", rect.height(), ") ");
1816 return rectString.toString();
1817}
1818
1819ExceptionOr<void> Internals::setMarkedTextMatchesAreHighlighted(bool flag)
1820{
1821 Document* document = contextDocument();
1822 if (!document || !document->frame())
1823 return Exception { InvalidAccessError };
1824 document->editor().setMarkedTextMatchesAreHighlighted(flag);
1825 return { };
1826}
1827
1828void Internals::invalidateFontCache()
1829{
1830 FontCache::invalidateAllFontCaches();
1831}
1832
1833void Internals::setFontSmoothingEnabled(bool enabled)
1834{
1835 FontCascade::setShouldUseSmoothing(enabled);
1836}
1837
1838ExceptionOr<void> Internals::setLowPowerModeEnabled(bool isEnabled)
1839{
1840 auto* document = contextDocument();
1841 if (!document)
1842 return Exception { InvalidAccessError };
1843 auto* page = document->page();
1844 if (!page)
1845 return Exception { InvalidAccessError };
1846
1847 page->setLowPowerModeEnabledOverrideForTesting(isEnabled);
1848 return { };
1849}
1850
1851ExceptionOr<void> Internals::setOutsideViewportThrottlingEnabled(bool isEnabled)
1852{
1853 auto* document = contextDocument();
1854 if (!document)
1855 return Exception { InvalidAccessError };
1856 auto* page = document->page();
1857 if (!page)
1858 return Exception { InvalidAccessError };
1859
1860 page->setOutsideViewportThrottlingEnabledForTesting(isEnabled);
1861 return { };
1862}
1863
1864ExceptionOr<void> Internals::setScrollViewPosition(int x, int y)
1865{
1866 Document* document = contextDocument();
1867 if (!document || !document->view())
1868 return Exception { InvalidAccessError };
1869
1870 auto& frameView = *document->view();
1871 auto oldClamping = frameView.scrollClamping();
1872 bool scrollbarsSuppressedOldValue = frameView.scrollbarsSuppressed();
1873
1874 frameView.setScrollClamping(ScrollClamping::Unclamped);
1875 frameView.setScrollbarsSuppressed(false);
1876 frameView.setScrollOffsetFromInternals({ x, y });
1877 frameView.setScrollbarsSuppressed(scrollbarsSuppressedOldValue);
1878 frameView.setScrollClamping(oldClamping);
1879
1880 return { };
1881}
1882
1883ExceptionOr<void> Internals::unconstrainedScrollTo(Element& element, double x, double y)
1884{
1885 Document* document = contextDocument();
1886 if (!document || !document->view())
1887 return Exception { InvalidAccessError };
1888
1889 element.scrollTo(ScrollToOptions(x, y), ScrollClamping::Unclamped);
1890
1891 auto& frameView = *document->view();
1892 frameView.setViewportConstrainedObjectsNeedLayout();
1893
1894 return { };
1895}
1896
1897ExceptionOr<void> Internals::scrollBySimulatingWheelEvent(Element& element, double deltaX, double deltaY)
1898{
1899 Document* document = contextDocument();
1900 if (!document || !document->view())
1901 return Exception { InvalidAccessError };
1902
1903 if (!element.renderBox())
1904 return Exception { InvalidAccessError };
1905
1906 RenderBox& box = *element.renderBox();
1907 ScrollableArea* scrollableArea;
1908
1909 if (&element == document->scrollingElementForAPI()) {
1910 FrameView* frameView = box.frame().mainFrame().view();
1911 if (!frameView || !frameView->isScrollable())
1912 return Exception { InvalidAccessError };
1913
1914 scrollableArea = frameView;
1915 } else {
1916 if (!box.canBeScrolledAndHasScrollableArea())
1917 return Exception { InvalidAccessError };
1918
1919 ASSERT(box.layer());
1920 scrollableArea = box.layer()->scrollableArea();
1921 }
1922
1923 if (!scrollableArea)
1924 return Exception { InvalidAccessError };
1925
1926 auto scrollingNodeID = scrollableArea->scrollingNodeID();
1927 if (!scrollingNodeID)
1928 return Exception { InvalidAccessError };
1929
1930 auto page = document->page();
1931 if (!page)
1932 return Exception { InvalidAccessError };
1933
1934 auto scrollingCoordinator = page->scrollingCoordinator();
1935 if (!scrollingCoordinator)
1936 return Exception { InvalidAccessError };
1937
1938 scrollingCoordinator->scrollBySimulatingWheelEventForTesting(scrollingNodeID, FloatSize(deltaX, deltaY));
1939
1940 return { };
1941}
1942
1943ExceptionOr<Ref<DOMRect>> Internals::layoutViewportRect()
1944{
1945 Document* document = contextDocument();
1946 if (!document || !document->frame())
1947 return Exception { InvalidAccessError };
1948
1949 document->updateLayoutIgnorePendingStylesheets();
1950
1951 auto& frameView = *document->view();
1952 return DOMRect::create(frameView.layoutViewportRect());
1953}
1954
1955ExceptionOr<Ref<DOMRect>> Internals::visualViewportRect()
1956{
1957 Document* document = contextDocument();
1958 if (!document || !document->frame())
1959 return Exception { InvalidAccessError };
1960
1961 document->updateLayoutIgnorePendingStylesheets();
1962
1963 auto& frameView = *document->view();
1964 return DOMRect::create(frameView.visualViewportRect());
1965}
1966
1967ExceptionOr<void> Internals::setViewIsTransparent(bool transparent)
1968{
1969 Document* document = contextDocument();
1970 if (!document || !document->view())
1971 return Exception { InvalidAccessError };
1972 std::optional<Color> backgroundColor;
1973 if (transparent)
1974 backgroundColor = Color(Color::transparentBlack);
1975 document->view()->updateBackgroundRecursively(backgroundColor);
1976 return { };
1977}
1978
1979ExceptionOr<String> Internals::viewBaseBackgroundColor()
1980{
1981 Document* document = contextDocument();
1982 if (!document || !document->view())
1983 return Exception { InvalidAccessError };
1984 return serializationForCSS(document->view()->baseBackgroundColor());
1985}
1986
1987ExceptionOr<void> Internals::setViewBaseBackgroundColor(const String& colorValue)
1988{
1989 Document* document = contextDocument();
1990 if (!document || !document->view())
1991 return Exception { InvalidAccessError };
1992
1993 if (colorValue == "transparent"_s) {
1994 document->view()->setBaseBackgroundColor(Color::transparentBlack);
1995 return { };
1996 }
1997 if (colorValue == "white"_s) {
1998 document->view()->setBaseBackgroundColor(Color::white);
1999 return { };
2000 }
2001 return Exception { SyntaxError };
2002}
2003
2004ExceptionOr<void> Internals::setPagination(const String& mode, int gap, int pageLength)
2005{
2006 Document* document = contextDocument();
2007 if (!document || !document->page())
2008 return Exception { InvalidAccessError };
2009
2010 Pagination pagination;
2011 if (mode == "Unpaginated"_s)
2012 pagination.mode = Pagination::Unpaginated;
2013 else if (mode == "LeftToRightPaginated"_s)
2014 pagination.mode = Pagination::LeftToRightPaginated;
2015 else if (mode == "RightToLeftPaginated"_s)
2016 pagination.mode = Pagination::RightToLeftPaginated;
2017 else if (mode == "TopToBottomPaginated"_s)
2018 pagination.mode = Pagination::TopToBottomPaginated;
2019 else if (mode == "BottomToTopPaginated"_s)
2020 pagination.mode = Pagination::BottomToTopPaginated;
2021 else
2022 return Exception { SyntaxError };
2023
2024 pagination.gap = gap;
2025 pagination.pageLength = pageLength;
2026 document->page()->setPagination(pagination);
2027
2028 return { };
2029}
2030
2031ExceptionOr<void> Internals::setPaginationLineGridEnabled(bool enabled)
2032{
2033 Document* document = contextDocument();
2034 if (!document || !document->page())
2035 return Exception { InvalidAccessError };
2036 document->page()->setPaginationLineGridEnabled(enabled);
2037 return { };
2038}
2039
2040ExceptionOr<uint64_t> Internals::lineIndexAfterPageBreak(Element& element)
2041{
2042 Document* document = contextDocument();
2043 if (!document || !document->frame())
2044 return Exception { InvalidAccessError };
2045
2046 element.document().updateLayoutIgnorePendingStylesheets();
2047
2048 if (!element.renderer() || !is<RenderBlockFlow>(element.renderer()))
2049 return Exception { NotFoundError };
2050 auto& blockFlow = downcast<RenderBlockFlow>(*element.renderer());
2051 if (!blockFlow.childrenInline())
2052 return Exception { NotFoundError };
2053
2054 size_t lineIndex = 0;
2055 for (auto lineBox = InlineIterator::firstLineBoxFor(blockFlow); lineBox; lineBox.traverseNext(), ++lineIndex) {
2056 if (lineBox->isFirstAfterPageBreak())
2057 return lineIndex;
2058 }
2059 return Exception { NotFoundError };
2060}
2061
2062ExceptionOr<String> Internals::configurationForViewport(float devicePixelRatio, int deviceWidth, int deviceHeight, int availableWidth, int availableHeight)
2063{
2064 Document* document = contextDocument();
2065 if (!document || !document->page())
2066 return Exception { InvalidAccessError };
2067
2068 const int defaultLayoutWidthForNonMobilePages = 980;
2069
2070 ViewportArguments arguments = document->page()->viewportArguments();
2071 ViewportAttributes attributes = computeViewportAttributes(arguments, defaultLayoutWidthForNonMobilePages, deviceWidth, deviceHeight, devicePixelRatio, IntSize(availableWidth, availableHeight));
2072 restrictMinimumScaleFactorToViewportSize(attributes, IntSize(availableWidth, availableHeight), devicePixelRatio);
2073 restrictScaleFactorToInitialScaleIfNotUserScalable(attributes);
2074
2075 // 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.
2076 return makeString("viewport size ", FormattedNumber::fixedPrecision(attributes.layoutSize.width()), 'x', FormattedNumber::fixedPrecision(attributes.layoutSize.height()), " scale ", FormattedNumber::fixedPrecision(attributes.initialScale), " with limits [", FormattedNumber::fixedPrecision(attributes.minimumScale), ", ", FormattedNumber::fixedPrecision(attributes.maximumScale), "] and userScalable ", (attributes.userScalable ? "true" : "false"));
2077}
2078
2079ExceptionOr<bool> Internals::wasLastChangeUserEdit(Element& textField)
2080{
2081 if (is<HTMLInputElement>(textField))
2082 return downcast<HTMLInputElement>(textField).lastChangeWasUserEdit();
2083
2084 if (is<HTMLTextAreaElement>(textField))
2085 return downcast<HTMLTextAreaElement>(textField).lastChangeWasUserEdit();
2086
2087 return Exception { InvalidNodeTypeError };
2088}
2089
2090bool Internals::elementShouldAutoComplete(HTMLInputElement& element)
2091{
2092 return element.shouldAutocomplete();
2093}
2094
2095void Internals::setAutofilled(HTMLInputElement& element, bool enabled)
2096{
2097 element.setAutoFilled(enabled);
2098}
2099
2100void Internals::setAutoFilledAndViewable(HTMLInputElement& element, bool enabled)
2101{
2102 element.setAutoFilledAndViewable(enabled);
2103}
2104
2105void Internals::setAutoFilledAndObscured(HTMLInputElement& element, bool enabled)
2106{
2107 element.setAutoFilledAndObscured(enabled);
2108}
2109
2110static AutoFillButtonType toAutoFillButtonType(Internals::AutoFillButtonType type)
2111{
2112 switch (type) {
2113 case Internals::AutoFillButtonType::None:
2114 return AutoFillButtonType::None;
2115 case Internals::AutoFillButtonType::Credentials:
2116 return AutoFillButtonType::Credentials;
2117 case Internals::AutoFillButtonType::Contacts:
2118 return AutoFillButtonType::Contacts;
2119 case Internals::AutoFillButtonType::StrongPassword:
2120 return AutoFillButtonType::StrongPassword;
2121 case Internals::AutoFillButtonType::CreditCard:
2122 return AutoFillButtonType::CreditCard;
2123 }
2124 ASSERT_NOT_REACHED();
2125 return AutoFillButtonType::None;
2126}
2127
2128static Internals::AutoFillButtonType toInternalsAutoFillButtonType(AutoFillButtonType type)
2129{
2130 switch (type) {
2131 case AutoFillButtonType::None:
2132 return Internals::AutoFillButtonType::None;
2133 case AutoFillButtonType::Credentials:
2134 return Internals::AutoFillButtonType::Credentials;
2135 case AutoFillButtonType::Contacts:
2136 return Internals::AutoFillButtonType::Contacts;
2137 case AutoFillButtonType::StrongPassword:
2138 return Internals::AutoFillButtonType::StrongPassword;
2139 case AutoFillButtonType::CreditCard:
2140 return Internals::AutoFillButtonType::CreditCard;
2141 }
2142 ASSERT_NOT_REACHED();
2143 return Internals::AutoFillButtonType::None;
2144}
2145
2146void Internals::setShowAutoFillButton(HTMLInputElement& element, AutoFillButtonType type)
2147{
2148 element.setShowAutoFillButton(toAutoFillButtonType(type));
2149}
2150
2151auto Internals::autoFillButtonType(const HTMLInputElement& element) -> AutoFillButtonType
2152{
2153 return toInternalsAutoFillButtonType(element.autoFillButtonType());
2154}
2155
2156auto Internals::lastAutoFillButtonType(const HTMLInputElement& element) -> AutoFillButtonType
2157{
2158 return toInternalsAutoFillButtonType(element.lastAutoFillButtonType());
2159}
2160
2161ExceptionOr<void> Internals::scrollElementToRect(Element& element, int x, int y, int w, int h)
2162{
2163 FrameView* frameView = element.document().view();
2164 if (!frameView)
2165 return Exception { InvalidAccessError };
2166 frameView->scrollElementToRect(element, { x, y, w, h });
2167 return { };
2168}
2169
2170ExceptionOr<String> Internals::autofillFieldName(Element& element)
2171{
2172 if (!is<HTMLFormControlElement>(element))
2173 return Exception { InvalidNodeTypeError };
2174
2175 return String { downcast<HTMLFormControlElement>(element).autofillData().fieldName };
2176}
2177
2178ExceptionOr<void> Internals::invalidateControlTints()
2179{
2180 Document* document = contextDocument();
2181 if (!document || !document->view())
2182 return Exception { InvalidAccessError };
2183
2184 document->view()->invalidateControlTints();
2185 return { };
2186}
2187
2188static TextIteratorBehaviors toTextIteratorBehaviors(const Vector<String>& stringBehaviors)
2189{
2190 TextIteratorBehaviors behaviors;
2191 for (const auto& stringBehavior : stringBehaviors) {
2192 if (stringBehavior == "IgnoresWhiteSpaceAtEndOfRun"_s)
2193 behaviors.add(TextIteratorBehavior::IgnoresWhiteSpaceAtEndOfRun);
2194 }
2195 return behaviors;
2196}
2197
2198RefPtr<Range> Internals::rangeFromLocationAndLength(Element& scope, unsigned rangeLocation, unsigned rangeLength)
2199{
2200 return createLiveRange(resolveCharacterRange(makeRangeSelectingNodeContents(scope), { rangeLocation, rangeLength }));
2201}
2202
2203unsigned Internals::locationFromRange(Element& scope, const Range& range, const Vector<String>& stringBehaviors)
2204{
2205 return clampTo<unsigned>(characterRange(makeBoundaryPointBeforeNodeContents(scope), makeSimpleRange(range), toTextIteratorBehaviors(stringBehaviors)).location);
2206}
2207
2208unsigned Internals::lengthFromRange(Element& scope, const Range& range, const Vector<String>& stringBehaviors)
2209{
2210 return clampTo<unsigned>(characterRange(makeBoundaryPointBeforeNodeContents(scope), makeSimpleRange(range), toTextIteratorBehaviors(stringBehaviors)).length);
2211}
2212
2213String Internals::rangeAsText(const Range& liveRange)
2214{
2215 auto range = makeSimpleRange(liveRange);
2216 range.start.document().updateLayout();
2217 return plainText(range);
2218}
2219
2220// FIXME: Move this to StringConcatenate.h.
2221static String join(Vector<String>&& strings)
2222{
2223 StringBuilder result;
2224 for (auto& string : strings)
2225 result.append(WTFMove(string));
2226 return result.toString();
2227}
2228
2229String Internals::rangeAsTextUsingBackwardsTextIterator(const Range& liveRange)
2230{
2231 auto range = makeSimpleRange(liveRange);
2232 range.start.document().updateLayout();
2233 Vector<String> strings;
2234 for (SimplifiedBackwardsTextIterator backwardsIterator(range); !backwardsIterator.atEnd(); backwardsIterator.advance())
2235 strings.append(backwardsIterator.text().toString());
2236 strings.reverse();
2237 return join(WTFMove(strings));
2238}
2239
2240Ref<Range> Internals::subrange(Range& liveRange, unsigned rangeLocation, unsigned rangeLength)
2241{
2242 auto range = makeSimpleRange(liveRange);
2243 range.start.document().updateLayout();
2244 return createLiveRange(resolveCharacterRange(range, { rangeLocation, rangeLength }));
2245}
2246
2247RefPtr<Range> Internals::rangeOfStringNearLocation(const Range& liveRange, const String& text, unsigned targetOffset)
2248{
2249 auto range = makeSimpleRange(liveRange);
2250 range.start.document().updateLayout();
2251 return createLiveRange(findClosestPlainText(range, text, { }, targetOffset));
2252}
2253
2254Vector<Internals::TextIteratorState> Internals::statesOfTextIterator(const Range& liveRange, const Vector<String>& stringBehaviors)
2255{
2256 auto simpleRange = makeSimpleRange(liveRange);
2257 simpleRange.start.document().updateLayout();
2258
2259 Vector<TextIteratorState> states;
2260 for (TextIterator it(simpleRange, toTextIteratorBehaviors(stringBehaviors)); !it.atEnd(); it.advance())
2261 states.append({ it.text().toString(), createLiveRange(it.range()) });
2262 return states;
2263}
2264
2265#if !PLATFORM(MAC)
2266ExceptionOr<RefPtr<Range>> Internals::rangeForDictionaryLookupAtLocation(int, int)
2267{
2268 return Exception { InvalidAccessError };
2269}
2270#endif
2271
2272ExceptionOr<void> Internals::setDelegatesScrolling(bool enabled)
2273{
2274 Document* document = contextDocument();
2275 // Delegate scrolling is valid only on mainframe's view.
2276 if (!document || !document->view() || !document->page() || &document->page()->mainFrame() != document->frame())
2277 return Exception { InvalidAccessError };
2278
2279 document->view()->setDelegatesScrolling(enabled);
2280 return { };
2281}
2282
2283ExceptionOr<uint64_t> Internals::lastSpellCheckRequestSequence()
2284{
2285 Document* document = contextDocument();
2286 if (!document || !document->frame())
2287 return Exception { InvalidAccessError };
2288
2289 return document->editor().spellChecker().lastRequestIdentifier().toUInt64();
2290}
2291
2292ExceptionOr<uint64_t> Internals::lastSpellCheckProcessedSequence()
2293{
2294 Document* document = contextDocument();
2295 if (!document || !document->frame())
2296 return Exception { InvalidAccessError };
2297
2298 return document->editor().spellChecker().lastProcessedIdentifier().toUInt64();
2299}
2300
2301void Internals::advanceToNextMisspelling()
2302{
2303#if !PLATFORM(IOS_FAMILY)
2304 if (auto* document = contextDocument())
2305 document->editor().advanceToNextMisspelling();
2306#endif
2307}
2308
2309Vector<String> Internals::userPreferredLanguages() const
2310{
2311 return WTF::userPreferredLanguages(ShouldMinimizeLanguages::No);
2312}
2313
2314void Internals::setUserPreferredLanguages(const Vector<String>& languages)
2315{
2316 overrideUserPreferredLanguages(languages);
2317}
2318
2319Vector<String> Internals::userPreferredAudioCharacteristics() const
2320{
2321 Document* document = contextDocument();
2322 if (!document || !document->page())
2323 return Vector<String>();
2324#if ENABLE(VIDEO)
2325 return document->page()->group().ensureCaptionPreferences().preferredAudioCharacteristics();
2326#else
2327 return Vector<String>();
2328#endif
2329}
2330
2331void Internals::setUserPreferredAudioCharacteristic(const String& characteristic)
2332{
2333 Document* document = contextDocument();
2334 if (!document || !document->page())
2335 return;
2336#if ENABLE(VIDEO)
2337 document->page()->group().ensureCaptionPreferences().setPreferredAudioCharacteristic(characteristic);
2338#else
2339 UNUSED_PARAM(characteristic);
2340#endif
2341}
2342
2343ExceptionOr<unsigned> Internals::wheelEventHandlerCount()
2344{
2345 Document* document = contextDocument();
2346 if (!document)
2347 return Exception { InvalidAccessError };
2348
2349 return document->wheelEventHandlerCount();
2350}
2351
2352ExceptionOr<unsigned> Internals::touchEventHandlerCount()
2353{
2354 Document* document = contextDocument();
2355 if (!document)
2356 return Exception { InvalidAccessError };
2357
2358 return document->touchEventHandlerCount();
2359}
2360
2361ExceptionOr<Ref<DOMRectList>> Internals::touchEventRectsForEvent(const String& eventName)
2362{
2363 Document* document = contextDocument();
2364 if (!document || !document->page())
2365 return Exception { InvalidAccessError };
2366
2367 std::array<EventTrackingRegions::EventType, 4> touchEvents = { {
2368 EventTrackingRegions::EventType::Touchstart,
2369 EventTrackingRegions::EventType::Touchmove,
2370 EventTrackingRegions::EventType::Touchend,
2371 EventTrackingRegions::EventType::Touchforcechange,
2372 } };
2373
2374 std::optional<EventTrackingRegions::EventType> touchEvent;
2375 for (auto event : touchEvents) {
2376 if (eventName == EventTrackingRegions::eventName(event)) {
2377 touchEvent = event;
2378 break;
2379 }
2380 }
2381
2382 if (!touchEvent)
2383 return Exception { InvalidAccessError };
2384
2385 return document->page()->touchEventRectsForEventForTesting(touchEvent.value());
2386}
2387
2388ExceptionOr<Ref<DOMRectList>> Internals::passiveTouchEventListenerRects()
2389{
2390 Document* document = contextDocument();
2391 if (!document || !document->page())
2392 return Exception { InvalidAccessError };
2393
2394 return document->page()->passiveTouchEventListenerRectsForTesting();
2395}
2396
2397// FIXME: Remove the document argument. It is almost always the same as
2398// contextDocument(), with the exception of a few tests that pass a
2399// different document, and could just make the call through another Internals
2400// instance instead.
2401ExceptionOr<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
2402{
2403 if (!document.frame() || !document.frame()->view())
2404 return Exception { InvalidAccessError };
2405
2406 Frame* frame = document.frame();
2407 FrameView* frameView = document.view();
2408 RenderView* renderView = document.renderView();
2409 if (!renderView)
2410 return nullptr;
2411
2412 document.updateLayoutIgnorePendingStylesheets();
2413
2414 float zoomFactor = frame->pageZoomFactor();
2415 LayoutPoint point(centerX * zoomFactor + frameView->scrollX(), centerY * zoomFactor + frameView->scrollY());
2416
2417 OptionSet<HitTestRequest::Type> hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::CollectMultipleElements };
2418 if (ignoreClipping)
2419 hitType.add(HitTestRequest::Type::IgnoreClipping);
2420 if (!allowUserAgentShadowContent)
2421 hitType.add(HitTestRequest::Type::DisallowUserAgentShadowContent);
2422 if (allowChildFrameContent)
2423 hitType.add(HitTestRequest::Type::AllowChildFrameContent);
2424
2425 HitTestRequest request(hitType);
2426
2427 auto hitTestResult = [&] {
2428 auto size = LayoutSize { leftPadding + rightPadding + 1, topPadding + bottomPadding + 1 };
2429 if (size.isEmpty())
2430 return HitTestResult { point };
2431 auto adjustedPosition = LayoutPoint { flooredIntPoint(point) } - LayoutSize { leftPadding, topPadding };
2432 return HitTestResult { LayoutRect { adjustedPosition, size } };
2433 }();
2434 // When ignoreClipping is false, this method returns null for coordinates outside of the viewport.
2435 if (!request.ignoreClipping() && !hitTestResult.hitTestLocation().intersects(LayoutRect { frameView->visibleContentRect() }))
2436 return nullptr;
2437
2438 document.hitTest(request, hitTestResult);
2439 auto matches = WTF::map(hitTestResult.listBasedTestResult(), [](const auto& node) { return node.copyRef(); });
2440 return RefPtr<NodeList> { StaticNodeList::create(WTFMove(matches)) };
2441}
2442
2443class GetCallerCodeBlockFunctor {
2444public:
2445 GetCallerCodeBlockFunctor()
2446 : m_iterations(0)
2447 , m_codeBlock(0)
2448 {
2449 }
2450
2451 IterationStatus operator()(StackVisitor& visitor) const
2452 {
2453 ++m_iterations;
2454 if (m_iterations < 2)
2455 return IterationStatus::Continue;
2456
2457 m_codeBlock = visitor->codeBlock();
2458 return IterationStatus::Done;
2459 }
2460
2461 CodeBlock* codeBlock() const { return m_codeBlock; }
2462
2463private:
2464 mutable int m_iterations;
2465 mutable CodeBlock* m_codeBlock;
2466};
2467
2468String Internals::parserMetaData(JSC::JSValue code)
2469{
2470 auto& vm = contextDocument()->vm();
2471 auto callFrame = vm.topCallFrame;
2472 auto* globalObject = callFrame->lexicalGlobalObject(vm);
2473
2474 ScriptExecutable* executable;
2475 if (!code || code.isNull() || code.isUndefined()) {
2476 GetCallerCodeBlockFunctor iter;
2477 callFrame->iterate(vm, iter);
2478 executable = iter.codeBlock()->ownerExecutable();
2479 } else if (code.isCallable())
2480 executable = JSC::jsCast<JSFunction*>(code.toObject(globalObject))->jsExecutable();
2481 else
2482 return String();
2483
2484 const char* prefix = "";
2485 String functionName;
2486 const char* suffix = "";
2487
2488 if (executable->isFunctionExecutable()) {
2489 prefix = "function \"";
2490 functionName = static_cast<FunctionExecutable*>(executable)->ecmaName().string();
2491 suffix = "\"";
2492 } else if (executable->isEvalExecutable())
2493 prefix = "eval";
2494 else if (executable->isModuleProgramExecutable())
2495 prefix = "module";
2496 else if (executable->isProgramExecutable())
2497 prefix = "program";
2498 else
2499 ASSERT_NOT_REACHED();
2500
2501 return makeString(prefix, functionName, suffix, " { ",
2502 executable->firstLine(), ':', executable->startColumn(), " - ",
2503 executable->lastLine(), ':', executable->endColumn(), " }");
2504}
2505
2506void Internals::updateEditorUINowIfScheduled()
2507{
2508 if (Document* document = contextDocument()) {
2509 if (Frame* frame = document->frame())
2510 frame->editor().updateEditorUINowIfScheduled();
2511 }
2512}
2513
2514bool Internals::hasSpellingMarker(int from, int length)
2515{
2516 Document* document = contextDocument();
2517 if (!document || !document->frame())
2518 return false;
2519
2520 updateEditorUINowIfScheduled();
2521
2522 return document->editor().selectionStartHasMarkerFor(DocumentMarker::Spelling, from, length);
2523}
2524
2525bool Internals::hasAutocorrectedMarker(int from, int length)
2526{
2527 Document* document = contextDocument();
2528 if (!document || !document->frame())
2529 return false;
2530
2531 updateEditorUINowIfScheduled();
2532
2533 return document->editor().selectionStartHasMarkerFor(DocumentMarker::Autocorrected, from, length);
2534}
2535
2536bool Internals::hasDictationAlternativesMarker(int from, int length)
2537{
2538 auto* document = contextDocument();
2539 if (!document || !document->frame())
2540 return false;
2541
2542 updateEditorUINowIfScheduled();
2543
2544 return document->frame()->editor().selectionStartHasMarkerFor(DocumentMarker::DictationAlternatives, from, length);
2545}
2546
2547void Internals::setContinuousSpellCheckingEnabled(bool enabled)
2548{
2549 if (!contextDocument() || !contextDocument()->frame())
2550 return;
2551
2552 if (enabled != contextDocument()->editor().isContinuousSpellCheckingEnabled())
2553 contextDocument()->editor().toggleContinuousSpellChecking();
2554}
2555
2556void Internals::setAutomaticQuoteSubstitutionEnabled(bool enabled)
2557{
2558 if (!contextDocument() || !contextDocument()->frame())
2559 return;
2560
2561#if USE(AUTOMATIC_TEXT_REPLACEMENT)
2562 if (enabled != contextDocument()->editor().isAutomaticQuoteSubstitutionEnabled())
2563 contextDocument()->editor().toggleAutomaticQuoteSubstitution();
2564#else
2565 UNUSED_PARAM(enabled);
2566#endif
2567}
2568
2569void Internals::setAutomaticLinkDetectionEnabled(bool enabled)
2570{
2571 if (!contextDocument() || !contextDocument()->frame())
2572 return;
2573
2574#if USE(AUTOMATIC_TEXT_REPLACEMENT)
2575 if (enabled != contextDocument()->editor().isAutomaticLinkDetectionEnabled())
2576 contextDocument()->editor().toggleAutomaticLinkDetection();
2577#else
2578 UNUSED_PARAM(enabled);
2579#endif
2580}
2581
2582bool Internals::testProcessIncomingSyncMessagesWhenWaitingForSyncReply()
2583{
2584 ASSERT(contextDocument());
2585 ASSERT(contextDocument()->page());
2586 return contextDocument()->page()->chrome().client().testProcessIncomingSyncMessagesWhenWaitingForSyncReply();
2587}
2588
2589void Internals::setAutomaticDashSubstitutionEnabled(bool enabled)
2590{
2591 if (!contextDocument() || !contextDocument()->frame())
2592 return;
2593
2594#if USE(AUTOMATIC_TEXT_REPLACEMENT)
2595 if (enabled != contextDocument()->editor().isAutomaticDashSubstitutionEnabled())
2596 contextDocument()->editor().toggleAutomaticDashSubstitution();
2597#else
2598 UNUSED_PARAM(enabled);
2599#endif
2600}
2601
2602void Internals::setAutomaticTextReplacementEnabled(bool enabled)
2603{
2604 if (!contextDocument() || !contextDocument()->frame())
2605 return;
2606
2607#if USE(AUTOMATIC_TEXT_REPLACEMENT)
2608 if (enabled != contextDocument()->editor().isAutomaticTextReplacementEnabled())
2609 contextDocument()->editor().toggleAutomaticTextReplacement();
2610#else
2611 UNUSED_PARAM(enabled);
2612#endif
2613}
2614
2615void Internals::setAutomaticSpellingCorrectionEnabled(bool enabled)
2616{
2617 if (!contextDocument() || !contextDocument()->frame())
2618 return;
2619
2620#if USE(AUTOMATIC_TEXT_REPLACEMENT)
2621 if (enabled != contextDocument()->editor().isAutomaticSpellingCorrectionEnabled())
2622 contextDocument()->editor().toggleAutomaticSpellingCorrection();
2623#else
2624 UNUSED_PARAM(enabled);
2625#endif
2626}
2627
2628bool Internals::isSpellcheckDisabledExceptTextReplacement(const HTMLInputElement& element) const
2629{
2630 return element.isSpellcheckDisabledExceptTextReplacement();
2631}
2632
2633void Internals::handleAcceptedCandidate(const String& candidate, unsigned location, unsigned length)
2634{
2635 if (!contextDocument() || !contextDocument()->frame())
2636 return;
2637
2638 TextCheckingResult result;
2639 result.type = TextCheckingType::None;
2640 result.range = { location, length };
2641 result.replacement = candidate;
2642 contextDocument()->editor().handleAcceptedCandidate(result);
2643}
2644
2645void Internals::changeSelectionListType()
2646{
2647 if (RefPtr frame = this->frame())
2648 frame->editor().changeSelectionListType();
2649}
2650
2651void Internals::changeBackToReplacedString(const String& replacedString)
2652{
2653 if (RefPtr frame = this->frame())
2654 frame->editor().changeBackToReplacedString(replacedString);
2655}
2656
2657bool Internals::isOverwriteModeEnabled()
2658{
2659 Document* document = contextDocument();
2660 if (!document || !document->frame())
2661 return false;
2662
2663 return document->editor().isOverwriteModeEnabled();
2664}
2665
2666void Internals::toggleOverwriteModeEnabled()
2667{
2668 Document* document = contextDocument();
2669 if (!document || !document->frame())
2670 return;
2671
2672 document->editor().toggleOverwriteModeEnabled();
2673}
2674
2675static ExceptionOr<FindOptions> parseFindOptions(const Vector<String>& optionList)
2676{
2677 const struct {
2678 ASCIILiteral name;
2679 FindOptionFlag value;
2680 } flagList[] = {
2681 { "CaseInsensitive"_s, CaseInsensitive },
2682 { "AtWordStarts"_s, AtWordStarts },
2683 { "TreatMedialCapitalAsWordStart"_s, TreatMedialCapitalAsWordStart },
2684 { "Backwards"_s, Backwards },
2685 { "WrapAround"_s, WrapAround },
2686 { "StartInSelection"_s, StartInSelection },
2687 { "DoNotRevealSelection"_s, DoNotRevealSelection },
2688 { "AtWordEnds"_s, AtWordEnds },
2689 { "DoNotTraverseFlatTree"_s, DoNotTraverseFlatTree },
2690 };
2691 FindOptions result;
2692 for (auto& option : optionList) {
2693 bool found = false;
2694 for (auto& flag : flagList) {
2695 if (flag.name == option) {
2696 result.add(flag.value);
2697 found = true;
2698 break;
2699 }
2700 }
2701 if (!found)
2702 return Exception { SyntaxError };
2703 }
2704 return result;
2705}
2706
2707ExceptionOr<RefPtr<Range>> Internals::rangeOfString(const String& text, RefPtr<Range>&& referenceRange, const Vector<String>& findOptions)
2708{
2709 Document* document = contextDocument();
2710 if (!document || !document->frame())
2711 return Exception { InvalidAccessError };
2712
2713 auto parsedOptions = parseFindOptions(findOptions);
2714 if (parsedOptions.hasException())
2715 return parsedOptions.releaseException();
2716
2717 return createLiveRange(document->editor().rangeOfString(text, makeSimpleRange(referenceRange), parsedOptions.releaseReturnValue()));
2718}
2719
2720ExceptionOr<unsigned> Internals::countMatchesForText(const String& text, const Vector<String>& findOptions, const String& markMatches)
2721{
2722 Document* document = contextDocument();
2723 if (!document || !document->frame())
2724 return Exception { InvalidAccessError };
2725
2726 auto parsedOptions = parseFindOptions(findOptions);
2727 if (parsedOptions.hasException())
2728 return parsedOptions.releaseException();
2729
2730 bool mark = markMatches == "mark"_s;
2731 return document->editor().countMatchesForText(text, std::nullopt, parsedOptions.releaseReturnValue(), 1000, mark, nullptr);
2732}
2733
2734ExceptionOr<unsigned> Internals::countFindMatches(const String& text, const Vector<String>& findOptions)
2735{
2736 Document* document = contextDocument();
2737 if (!document || !document->page())
2738 return Exception { InvalidAccessError };
2739
2740 auto parsedOptions = parseFindOptions(findOptions);
2741 if (parsedOptions.hasException())
2742 return parsedOptions.releaseException();
2743
2744 return document->page()->countFindMatches(text, parsedOptions.releaseReturnValue(), 1000);
2745}
2746
2747unsigned Internals::numberOfIDBTransactions() const
2748{
2749 return IDBTransaction::numberOfIDBTransactions;
2750}
2751
2752unsigned Internals::numberOfLiveNodes() const
2753{
2754 unsigned nodeCount = 0;
2755 for (auto* document : Document::allDocuments())
2756 nodeCount += document->referencingNodeCount();
2757 return nodeCount;
2758}
2759
2760unsigned Internals::numberOfLiveDocuments() const
2761{
2762 return Document::allDocuments().size();
2763}
2764
2765unsigned Internals::referencingNodeCount(const Document& document) const
2766{
2767 return document.referencingNodeCount();
2768}
2769
2770#if ENABLE(WEB_AUDIO)
2771uint64_t Internals::baseAudioContextIdentifier(const BaseAudioContext& context)
2772{
2773 return context.contextID();
2774}
2775
2776bool Internals::isBaseAudioContextAlive(uint64_t contextID)
2777{
2778 ASSERT(contextID);
2779 return BaseAudioContext::isContextAlive(contextID);
2780}
2781#endif // ENABLE(WEB_AUDIO)
2782
2783unsigned Internals::numberOfIntersectionObservers(const Document& document) const
2784{
2785 return document.numberOfIntersectionObservers();
2786}
2787
2788unsigned Internals::numberOfResizeObservers(const Document& document) const
2789{
2790 return document.numberOfResizeObservers();
2791}
2792
2793String Internals::documentIdentifier(const Document& document) const
2794{
2795 return document.identifier().object().toString();
2796}
2797
2798bool Internals::isDocumentAlive(const String& documentIdentifier) const
2799{
2800 auto uuid = UUID::parseVersion4(documentIdentifier);
2801 ASSERT(uuid);
2802 return uuid ? Document::allDocumentsMap().contains({ *uuid, Process::identifier() }) : false;
2803}
2804
2805uint64_t Internals::storageAreaMapCount() const
2806{
2807 auto* page = contextDocument() ? contextDocument()->page() : nullptr;
2808 if (!page)
2809 return 0;
2810
2811 return page->storageNamespaceProvider().localStorageNamespace(page->sessionID()).storageAreaMapCountForTesting();
2812}
2813
2814uint64_t Internals::elementIdentifier(Element& element) const
2815{
2816 return element.identifier().toUInt64();
2817}
2818
2819bool Internals::isElementAlive(uint64_t elementIdentifier) const
2820{
2821 return Element::fromIdentifier(makeObjectIdentifier<ElementIdentifierType>(elementIdentifier));
2822}
2823
2824uint64_t Internals::frameIdentifier(const Document& document) const
2825{
2826 if (auto* page = document.page())
2827 return valueOrDefault(page->mainFrame().loader().frameID()).toUInt64();
2828 return 0;
2829}
2830
2831uint64_t Internals::pageIdentifier(const Document& document) const
2832{
2833 return valueOrDefault(document.pageID()).toUInt64();
2834}
2835
2836bool Internals::isAnyWorkletGlobalScopeAlive() const
2837{
2838 return WorkletGlobalScope::numberOfWorkletGlobalScopes();
2839}
2840
2841String Internals::serviceWorkerClientInternalIdentifier(const Document& document) const
2842{
2843 return document.identifier().toString();
2844}
2845
2846RefPtr<WindowProxy> Internals::openDummyInspectorFrontend(const String& url)
2847{
2848 auto* inspectedPage = contextDocument()->frame()->page();
2849 auto* window = inspectedPage->mainFrame().document()->domWindow();
2850 auto frontendWindowProxy = window->open(*window, *window, url, emptyAtom(), emptyString()).releaseReturnValue();
2851 m_inspectorFrontend = makeUnique<InspectorStubFrontend>(*inspectedPage, downcast<DOMWindow>(frontendWindowProxy->window()));
2852 return frontendWindowProxy;
2853}
2854
2855void Internals::closeDummyInspectorFrontend()
2856{
2857 m_inspectorFrontend = nullptr;
2858}
2859
2860ExceptionOr<void> Internals::setInspectorIsUnderTest(bool isUnderTest)
2861{
2862 Page* page = contextDocument()->frame()->page();
2863 if (!page)
2864 return Exception { InvalidAccessError };
2865
2866 page->inspectorController().setIsUnderTest(isUnderTest);
2867 return { };
2868}
2869
2870bool Internals::hasGrammarMarker(int from, int length)
2871{
2872 Document* document = contextDocument();
2873 if (!document || !document->frame())
2874 return false;
2875
2876 return document->editor().selectionStartHasMarkerFor(DocumentMarker::Grammar, from, length);
2877}
2878
2879unsigned Internals::numberOfScrollableAreas()
2880{
2881 Document* document = contextDocument();
2882 if (!document || !document->frame())
2883 return 0;
2884
2885 unsigned count = 0;
2886 Frame* frame = document->frame();
2887 if (frame->view()->scrollableAreas())
2888 count += frame->view()->scrollableAreas()->size();
2889
2890 for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
2891 if (child->view() && child->view()->scrollableAreas())
2892 count += child->view()->scrollableAreas()->size();
2893 }
2894
2895 return count;
2896}
2897
2898ExceptionOr<bool> Internals::isPageBoxVisible(int pageNumber)
2899{
2900 Document* document = contextDocument();
2901 if (!document)
2902 return Exception { InvalidAccessError };
2903
2904 return document->isPageBoxVisible(pageNumber);
2905}
2906
2907static OptionSet<LayerTreeAsTextOptions> toLayerTreeAsTextOptions(unsigned short flags)
2908{
2909 OptionSet<LayerTreeAsTextOptions> layerTreeFlags;
2910 if (flags & Internals::LAYER_TREE_INCLUDES_VISIBLE_RECTS)
2911 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeVisibleRects);
2912 if (flags & Internals::LAYER_TREE_INCLUDES_TILE_CACHES)
2913 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeTileCaches);
2914 if (flags & Internals::LAYER_TREE_INCLUDES_REPAINT_RECTS)
2915 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeRepaintRects);
2916 if (flags & Internals::LAYER_TREE_INCLUDES_PAINTING_PHASES)
2917 layerTreeFlags.add(LayerTreeAsTextOptions::IncludePaintingPhases);
2918 if (flags & Internals::LAYER_TREE_INCLUDES_CONTENT_LAYERS)
2919 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeContentLayers);
2920 if (flags & Internals::LAYER_TREE_INCLUDES_ACCELERATES_DRAWING)
2921 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeAcceleratesDrawing);
2922 if (flags & Internals::LAYER_TREE_INCLUDES_CLIPPING)
2923 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeClipping);
2924 if (flags & Internals::LAYER_TREE_INCLUDES_BACKING_STORE_ATTACHED)
2925 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeBackingStoreAttached);
2926 if (flags & Internals::LAYER_TREE_INCLUDES_ROOT_LAYER_PROPERTIES)
2927 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeRootLayerProperties);
2928 if (flags & Internals::LAYER_TREE_INCLUDES_EVENT_REGION)
2929 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeEventRegion);
2930 if (flags & Internals::LAYER_TREE_INCLUDES_DEEP_COLOR)
2931 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeDeepColor);
2932 if (flags & Internals::LAYER_TREE_INCLUDES_DEVICE_SCALE)
2933 layerTreeFlags.add(LayerTreeAsTextOptions::IncludeDeviceScale);
2934
2935 return layerTreeFlags;
2936}
2937
2938// FIXME: Remove the document argument. It is almost always the same as
2939// contextDocument(), with the exception of a few tests that pass a
2940// different document, and could just make the call through another Internals
2941// instance instead.
2942ExceptionOr<String> Internals::layerTreeAsText(Document& document, unsigned short flags) const
2943{
2944 if (!document.frame() || !document.frame()->contentRenderer())
2945 return Exception { InvalidAccessError };
2946 return document.frame()->contentRenderer()->compositor().layerTreeAsText(toLayerTreeAsTextOptions(flags));
2947}
2948
2949ExceptionOr<uint64_t> Internals::layerIDForElement(Element& element)
2950{
2951 Document* document = contextDocument();
2952 if (!document || !document->frame())
2953 return Exception { InvalidAccessError };
2954
2955 element.document().updateLayoutIgnorePendingStylesheets();
2956
2957 if (!element.renderer() || !element.renderer()->hasLayer())
2958 return Exception { NotFoundError };
2959
2960 auto& layerModelObject = downcast<RenderLayerModelObject>(*element.renderer());
2961 if (!layerModelObject.layer()->isComposited())
2962 return Exception { NotFoundError };
2963
2964 auto* backing = layerModelObject.layer()->backing();
2965 return backing->graphicsLayer()->primaryLayerID();
2966}
2967
2968static OptionSet<PlatformLayerTreeAsTextFlags> toPlatformLayerTreeFlags(unsigned short flags)
2969{
2970 OptionSet<PlatformLayerTreeAsTextFlags> platformLayerTreeFlags = { };
2971 if (flags & Internals::PLATFORM_LAYER_TREE_DEBUG)
2972 platformLayerTreeFlags.add(PlatformLayerTreeAsTextFlags::Debug);
2973 if (flags & Internals::PLATFORM_LAYER_TREE_IGNORES_CHILDREN)
2974 platformLayerTreeFlags.add(PlatformLayerTreeAsTextFlags::IgnoreChildren);
2975 if (flags & Internals::PLATFORM_LAYER_TREE_INCLUDE_MODELS)
2976 platformLayerTreeFlags.add(PlatformLayerTreeAsTextFlags::IncludeModels);
2977 return platformLayerTreeFlags;
2978}
2979
2980ExceptionOr<String> Internals::platformLayerTreeAsText(Element& element, unsigned short flags) const
2981{
2982 Document& document = element.document();
2983 if (!document.frame() || !document.frame()->contentRenderer())
2984 return Exception { InvalidAccessError };
2985
2986 auto text = document.frame()->contentRenderer()->compositor().platformLayerTreeAsText(element, toPlatformLayerTreeFlags(flags));
2987 if (!text)
2988 return Exception { NotFoundError };
2989
2990 return String { text.value() };
2991}
2992
2993ExceptionOr<String> Internals::repaintRectsAsText() const
2994{
2995 Document* document = contextDocument();
2996 if (!document || !document->frame())
2997 return Exception { InvalidAccessError };
2998
2999 return document->frame()->trackedRepaintRectsAsText();
3000}
3001
3002ExceptionOr<ScrollableArea*> Internals::scrollableAreaForNode(Node* node) const
3003{
3004 if (!node)
3005 node = contextDocument();
3006
3007 if (!node)
3008 return Exception { InvalidAccessError };
3009
3010 Ref nodeRef { *node };
3011 nodeRef->document().updateLayoutIgnorePendingStylesheets();
3012
3013 ScrollableArea* scrollableArea = nullptr;
3014 if (is<Document>(nodeRef)) {
3015 auto* frameView = downcast<Document>(nodeRef.get()).view();
3016 if (!frameView)
3017 return Exception { InvalidAccessError };
3018
3019 scrollableArea = frameView;
3020 } else if (node == nodeRef->document().scrollingElement()) {
3021 auto* frameView = nodeRef->document().view();
3022 if (!frameView)
3023 return Exception { InvalidAccessError };
3024
3025 scrollableArea = frameView;
3026 } else if (is<Element>(nodeRef)) {
3027 auto& element = downcast<Element>(nodeRef.get());
3028 if (!element.renderBox())
3029 return Exception { InvalidAccessError };
3030
3031 auto& renderBox = *element.renderBox();
3032 if (!renderBox.canBeScrolledAndHasScrollableArea())
3033 return Exception { InvalidAccessError };
3034
3035 if (is<RenderListBox>(renderBox))
3036 scrollableArea = &downcast<RenderListBox>(renderBox);
3037 else {
3038 ASSERT(renderBox.layer());
3039 scrollableArea = renderBox.layer()->scrollableArea();
3040 }
3041 } else
3042 return Exception { InvalidNodeTypeError };
3043
3044 if (!scrollableArea)
3045 return Exception { InvalidNodeTypeError };
3046
3047 return scrollableArea;
3048}
3049
3050ExceptionOr<String> Internals::scrollbarOverlayStyle(Node* node) const
3051{
3052 auto areaOrException = scrollableAreaForNode(node);
3053 if (areaOrException.hasException())
3054 return areaOrException.releaseException();
3055
3056 auto* scrollableArea = areaOrException.releaseReturnValue();
3057 switch (scrollableArea->scrollbarOverlayStyle()) {
3058 case ScrollbarOverlayStyleDefault:
3059 return "default"_str;
3060 case ScrollbarOverlayStyleDark:
3061 return "dark"_str;
3062 case ScrollbarOverlayStyleLight:
3063 return "light"_str;
3064 }
3065
3066 ASSERT_NOT_REACHED();
3067 return "unknown"_str;
3068}
3069
3070ExceptionOr<bool> Internals::scrollbarUsingDarkAppearance(Node* node) const
3071{
3072 auto areaOrException = scrollableAreaForNode(node);
3073 if (areaOrException.hasException())
3074 return areaOrException.releaseException();
3075
3076 auto* scrollableArea = areaOrException.releaseReturnValue();
3077 return scrollableArea->useDarkAppearance();
3078}
3079
3080ExceptionOr<String> Internals::horizontalScrollbarState(Node* node) const
3081{
3082 auto areaOrException = scrollableAreaForNode(node);
3083 if (areaOrException.hasException())
3084 return areaOrException.releaseException();
3085
3086 auto* scrollableArea = areaOrException.releaseReturnValue();
3087 return scrollableArea->horizontalScrollbarStateForTesting();
3088}
3089
3090ExceptionOr<String> Internals::verticalScrollbarState(Node* node) const
3091{
3092 auto areaOrException = scrollableAreaForNode(node);
3093 if (areaOrException.hasException())
3094 return areaOrException.releaseException();
3095
3096 auto* scrollableArea = areaOrException.releaseReturnValue();
3097 return scrollableArea->verticalScrollbarStateForTesting();
3098}
3099
3100ExceptionOr<String> Internals::scrollingStateTreeAsText() const
3101{
3102 Document* document = contextDocument();
3103 if (!document || !document->frame())
3104 return Exception { InvalidAccessError };
3105
3106 document->updateLayoutIgnorePendingStylesheets();
3107
3108 Page* page = document->page();
3109 if (!page)
3110 return String();
3111
3112 return page->scrollingStateTreeAsText();
3113}
3114
3115ExceptionOr<String> Internals::scrollingTreeAsText() const
3116{
3117 Document* document = contextDocument();
3118 if (!document || !document->frame())
3119 return Exception { InvalidAccessError };
3120
3121 document->updateLayoutIgnorePendingStylesheets();
3122
3123 auto page = document->page();
3124 if (!page)
3125 return String();
3126
3127 auto scrollingCoordinator = page->scrollingCoordinator();
3128 if (!scrollingCoordinator)
3129 return String();
3130
3131 scrollingCoordinator->commitTreeStateIfNeeded();
3132 return scrollingCoordinator->scrollingTreeAsText();
3133}
3134
3135ExceptionOr<String> Internals::synchronousScrollingReasons() const
3136{
3137 Document* document = contextDocument();
3138 if (!document || !document->frame())
3139 return Exception { InvalidAccessError };
3140
3141 Page* page = document->page();
3142 if (!page)
3143 return String();
3144
3145 return page->synchronousScrollingReasonsAsText();
3146}
3147
3148ExceptionOr<Ref<DOMRectList>> Internals::nonFastScrollableRects() const
3149{
3150 Document* document = contextDocument();
3151 if (!document || !document->frame())
3152 return Exception { InvalidAccessError };
3153
3154 Page* page = document->page();
3155 if (!page)
3156 return DOMRectList::create();
3157
3158 return page->nonFastScrollableRectsForTesting();
3159}
3160
3161ExceptionOr<void> Internals::setElementUsesDisplayListDrawing(Element& element, bool usesDisplayListDrawing)
3162{
3163 Document* document = contextDocument();
3164 if (!document || !document->renderView())
3165 return Exception { InvalidAccessError };
3166
3167 element.document().updateLayoutIgnorePendingStylesheets();
3168
3169 if (!element.renderer())
3170 return Exception { InvalidAccessError };
3171
3172 if (is<HTMLCanvasElement>(element)) {
3173 downcast<HTMLCanvasElement>(element).setUsesDisplayListDrawing(usesDisplayListDrawing);
3174 return { };
3175 }
3176
3177 if (!element.renderer()->hasLayer())
3178 return Exception { InvalidAccessError };
3179
3180 RenderLayer* layer = downcast<RenderLayerModelObject>(element.renderer())->layer();
3181 if (!layer->isComposited())
3182 return Exception { InvalidAccessError };
3183
3184 layer->backing()->setUsesDisplayListDrawing(usesDisplayListDrawing);
3185 return { };
3186}
3187
3188ExceptionOr<void> Internals::setElementTracksDisplayListReplay(Element& element, bool isTrackingReplay)
3189{
3190 Document* document = contextDocument();
3191 if (!document || !document->renderView())
3192 return Exception { InvalidAccessError };
3193
3194 element.document().updateLayoutIgnorePendingStylesheets();
3195
3196 if (!element.renderer())
3197 return Exception { InvalidAccessError };
3198
3199 if (!element.renderer()->hasLayer())
3200 return Exception { InvalidAccessError };
3201
3202 RenderLayer* layer = downcast<RenderLayerModelObject>(element.renderer())->layer();
3203 if (!layer->isComposited())
3204 return Exception { InvalidAccessError };
3205
3206 layer->backing()->setIsTrackingDisplayListReplay(isTrackingReplay);
3207 return { };
3208}
3209
3210static OptionSet<DisplayList::AsTextFlag> toDisplayListFlags(unsigned short flags)
3211{
3212 OptionSet<DisplayList::AsTextFlag> displayListFlags;
3213 if (flags & Internals::DISPLAY_LIST_INCLUDE_PLATFORM_OPERATIONS)
3214 displayListFlags.add(DisplayList::AsTextFlag::IncludePlatformOperations);
3215 if (flags & Internals::DISPLAY_LIST_INCLUDE_RESOURCE_IDENTIFIERS)
3216 displayListFlags.add(DisplayList::AsTextFlag::IncludeResourceIdentifiers);
3217 return displayListFlags;
3218}
3219
3220ExceptionOr<String> Internals::displayListForElement(Element& element, unsigned short flags)
3221{
3222 Document* document = contextDocument();
3223 if (!document || !document->renderView())
3224 return Exception { InvalidAccessError };
3225
3226 element.document().updateLayoutIgnorePendingStylesheets();
3227
3228 if (!element.renderer())
3229 return Exception { InvalidAccessError };
3230
3231 if (!element.renderer()->hasLayer())
3232 return Exception { InvalidAccessError };
3233
3234 RenderLayer* layer = downcast<RenderLayerModelObject>(element.renderer())->layer();
3235 if (!layer->isComposited())
3236 return Exception { InvalidAccessError };
3237
3238 return layer->backing()->displayListAsText(toDisplayListFlags(flags));
3239}
3240
3241ExceptionOr<String> Internals::replayDisplayListForElement(Element& element, unsigned short flags)
3242{
3243 Document* document = contextDocument();
3244 if (!document || !document->renderView())
3245 return Exception { InvalidAccessError };
3246
3247 element.document().updateLayoutIgnorePendingStylesheets();
3248
3249 if (!element.renderer())
3250 return Exception { InvalidAccessError };
3251
3252 if (!element.renderer()->hasLayer())
3253 return Exception { InvalidAccessError };
3254
3255 RenderLayer* layer = downcast<RenderLayerModelObject>(element.renderer())->layer();
3256 if (!layer->isComposited())
3257 return Exception { InvalidAccessError };
3258
3259 return layer->backing()->replayDisplayListAsText(toDisplayListFlags(flags));
3260}
3261
3262void Internals::setForceUseGlyphDisplayListForTesting(bool enabled)
3263{
3264 TextPainter::setForceUseGlyphDisplayListForTesting(enabled);
3265}
3266
3267ExceptionOr<String> Internals::cachedGlyphDisplayListsForTextNode(Node& node, unsigned short flags)
3268{
3269 Document* document = contextDocument();
3270 if (!document || !document->renderView())
3271 return Exception { InvalidAccessError };
3272
3273 if (!is<Text>(node))
3274 return Exception { InvalidAccessError };
3275
3276 node.document().updateLayoutIgnorePendingStylesheets();
3277
3278 if (!node.renderer())
3279 return Exception { InvalidAccessError };
3280
3281 return TextPainter::cachedGlyphDisplayListsForTextNodeAsText(downcast<Text>(node), toDisplayListFlags(flags));
3282}
3283
3284ExceptionOr<void> Internals::garbageCollectDocumentResources() const
3285{
3286 Document* document = contextDocument();
3287 if (!document)
3288 return Exception { InvalidAccessError };
3289 document->cachedResourceLoader().garbageCollectDocumentResources();
3290 return { };
3291}
3292
3293bool Internals::isUnderMemoryPressure()
3294{
3295 return MemoryPressureHandler::singleton().isUnderMemoryPressure();
3296}
3297
3298void Internals::beginSimulatedMemoryPressure()
3299{
3300 MemoryPressureHandler::singleton().beginSimulatedMemoryPressure();
3301}
3302
3303void Internals::endSimulatedMemoryPressure()
3304{
3305 MemoryPressureHandler::singleton().endSimulatedMemoryPressure();
3306}
3307
3308ExceptionOr<void> Internals::insertAuthorCSS(const String& css) const
3309{
3310 Document* document = contextDocument();
3311 if (!document)
3312 return Exception { InvalidAccessError };
3313
3314 auto parsedSheet = StyleSheetContents::create(*document);
3315 parsedSheet.get().setIsUserStyleSheet(false);
3316 parsedSheet.get().parseString(css);
3317 document->extensionStyleSheets().addAuthorStyleSheetForTesting(WTFMove(parsedSheet));
3318 return { };
3319}
3320
3321ExceptionOr<void> Internals::insertUserCSS(const String& css) const
3322{
3323 Document* document = contextDocument();
3324 if (!document)
3325 return Exception { InvalidAccessError };
3326
3327 auto parsedSheet = StyleSheetContents::create(*document);
3328 parsedSheet.get().setIsUserStyleSheet(true);
3329 parsedSheet.get().parseString(css);
3330 document->extensionStyleSheets().addUserStyleSheet(WTFMove(parsedSheet));
3331 return { };
3332}
3333
3334String Internals::counterValue(Element& element)
3335{
3336 return counterValueForElement(&element);
3337}
3338
3339int Internals::pageNumber(Element& element, float pageWidth, float pageHeight)
3340{
3341 return PrintContext::pageNumberForElement(&element, { pageWidth, pageHeight });
3342}
3343
3344Vector<String> Internals::shortcutIconURLs() const
3345{
3346 if (!frame())
3347 return { };
3348
3349 auto* documentLoader = frame()->loader().documentLoader();
3350 if (!documentLoader)
3351 return { };
3352
3353 Vector<String> result;
3354 for (auto& linkIcon : documentLoader->linkIcons())
3355 result.append(linkIcon.url.string());
3356
3357 return result;
3358}
3359
3360int Internals::numberOfPages(float pageWidth, float pageHeight)
3361{
3362 if (!frame())
3363 return -1;
3364
3365 return PrintContext::numberOfPages(*frame(), FloatSize(pageWidth, pageHeight));
3366}
3367
3368ExceptionOr<String> Internals::pageProperty(const String& propertyName, int pageNumber) const
3369{
3370 if (!frame())
3371 return Exception { InvalidAccessError };
3372
3373 return PrintContext::pageProperty(frame(), propertyName.utf8().data(), pageNumber);
3374}
3375
3376ExceptionOr<String> Internals::pageSizeAndMarginsInPixels(int pageNumber, int width, int height, int marginTop, int marginRight, int marginBottom, int marginLeft) const
3377{
3378 if (!frame())
3379 return Exception { InvalidAccessError };
3380
3381 return PrintContext::pageSizeAndMarginsInPixels(frame(), pageNumber, width, height, marginTop, marginRight, marginBottom, marginLeft);
3382}
3383
3384ExceptionOr<float> Internals::pageScaleFactor() const
3385{
3386 Document* document = contextDocument();
3387 if (!document || !document->page())
3388 return Exception { InvalidAccessError };
3389
3390 return document->page()->pageScaleFactor();
3391}
3392
3393ExceptionOr<void> Internals::setPageScaleFactor(float scaleFactor, int x, int y)
3394{
3395 Document* document = contextDocument();
3396 if (!document || !document->page())
3397 return Exception { InvalidAccessError };
3398
3399 document->page()->setPageScaleFactor(scaleFactor, IntPoint(x, y));
3400 return { };
3401}
3402
3403ExceptionOr<void> Internals::setPageZoomFactor(float zoomFactor)
3404{
3405 Document* document = contextDocument();
3406 if (!document || !document->frame())
3407 return Exception { InvalidAccessError };
3408
3409 document->frame()->setPageZoomFactor(zoomFactor);
3410 return { };
3411}
3412
3413ExceptionOr<void> Internals::setTextZoomFactor(float zoomFactor)
3414{
3415 Document* document = contextDocument();
3416 if (!document || !document->frame())
3417 return Exception { InvalidAccessError };
3418
3419 document->frame()->setTextZoomFactor(zoomFactor);
3420 return { };
3421}
3422
3423ExceptionOr<void> Internals::setUseFixedLayout(bool useFixedLayout)
3424{
3425 Document* document = contextDocument();
3426 if (!document || !document->view())
3427 return Exception { InvalidAccessError };
3428
3429 document->view()->setUseFixedLayout(useFixedLayout);
3430 return { };
3431}
3432
3433ExceptionOr<void> Internals::setFixedLayoutSize(int width, int height)
3434{
3435 Document* document = contextDocument();
3436 if (!document || !document->view())
3437 return Exception { InvalidAccessError };
3438
3439 document->view()->setFixedLayoutSize(IntSize(width, height));
3440 return { };
3441}
3442
3443ExceptionOr<void> Internals::setViewExposedRect(float x, float y, float width, float height)
3444{
3445 Document* document = contextDocument();
3446 if (!document || !document->view())
3447 return Exception { InvalidAccessError };
3448
3449 document->view()->setViewExposedRect(FloatRect(x, y, width, height));
3450 return { };
3451}
3452
3453void Internals::setPrinting(int width, int height)
3454{
3455 printContextForTesting() = makeUnique<PrintContext>(frame());
3456 printContextForTesting()->begin(width, height);
3457}
3458
3459void Internals::setHeaderHeight(float height)
3460{
3461 Document* document = contextDocument();
3462 if (!document || !document->view())
3463 return;
3464
3465 document->page()->setHeaderHeight(height);
3466}
3467
3468void Internals::setFooterHeight(float height)
3469{
3470 Document* document = contextDocument();
3471 if (!document || !document->view())
3472 return;
3473
3474 document->page()->setFooterHeight(height);
3475}
3476
3477void Internals::setTopContentInset(float contentInset)
3478{
3479 Document* document = contextDocument();
3480 if (!document || !document->page())
3481 return;
3482
3483 document->page()->setTopContentInset(contentInset);
3484}
3485
3486#if ENABLE(FULLSCREEN_API)
3487
3488void Internals::webkitWillEnterFullScreenForElement(Element& element)
3489{
3490 Document* document = contextDocument();
3491 if (!document)
3492 return;
3493 document->fullscreenManager().willEnterFullscreen(element);
3494}
3495
3496void Internals::webkitDidEnterFullScreenForElement(Element&)
3497{
3498 Document* document = contextDocument();
3499 if (!document)
3500 return;
3501 document->fullscreenManager().didEnterFullscreen();
3502}
3503
3504void Internals::webkitWillExitFullScreenForElement(Element&)
3505{
3506 Document* document = contextDocument();
3507 if (!document)
3508 return;
3509 document->fullscreenManager().willExitFullscreen();
3510}
3511
3512void Internals::webkitDidExitFullScreenForElement(Element&)
3513{
3514 Document* document = contextDocument();
3515 if (!document)
3516 return;
3517 document->fullscreenManager().didExitFullscreen();
3518}
3519
3520bool Internals::isAnimatingFullScreen() const
3521{
3522 Document* document = contextDocument();
3523 if (!document)
3524 return false;
3525 return document->fullscreenManager().isAnimatingFullscreen();
3526}
3527
3528#endif
3529
3530void Internals::setFullscreenInsets(FullscreenInsets insets)
3531{
3532 Page* page = contextDocument()->frame()->page();
3533 ASSERT(page);
3534
3535 page->setFullscreenInsets(FloatBoxExtent(insets.top, insets.right, insets.bottom, insets.left));
3536}
3537
3538void Internals::setFullscreenAutoHideDuration(double duration)
3539{
3540 Page* page = contextDocument()->frame()->page();
3541 ASSERT(page);
3542
3543 page->setFullscreenAutoHideDuration(Seconds(duration));
3544}
3545
3546void Internals::setFullscreenControlsHidden(bool hidden)
3547{
3548 Page* page = contextDocument()->frame()->page();
3549 ASSERT(page);
3550
3551 page->setFullscreenControlsHidden(hidden);
3552}
3553
3554#if ENABLE(VIDEO)
3555bool Internals::isChangingPresentationMode(HTMLVideoElement& element) const
3556{
3557#if ENABLE(VIDEO_PRESENTATION_MODE)
3558 return element.isChangingPresentationMode();
3559#else
3560 UNUSED_PARAM(element);
3561 return false;
3562#endif
3563}
3564#endif
3565
3566#if ENABLE(VIDEO_PRESENTATION_MODE)
3567void Internals::setMockVideoPresentationModeEnabled(bool enabled)
3568{
3569 Document* document = contextDocument();
3570 if (!document || !document->page())
3571 return;
3572
3573 document->page()->chrome().client().setMockVideoPresentationModeEnabled(enabled);
3574}
3575#endif
3576
3577void Internals::setApplicationCacheOriginQuota(unsigned long long quota)
3578{
3579 Document* document = contextDocument();
3580 if (!document || !document->page())
3581 return;
3582 document->page()->applicationCacheStorage().storeUpdatedQuotaForOrigin(&document->securityOrigin(), quota);
3583}
3584
3585void Internals::registerURLSchemeAsBypassingContentSecurityPolicy(const String& scheme)
3586{
3587 LegacySchemeRegistry::registerURLSchemeAsBypassingContentSecurityPolicy(scheme);
3588}
3589
3590void Internals::removeURLSchemeRegisteredAsBypassingContentSecurityPolicy(const String& scheme)
3591{
3592 LegacySchemeRegistry::removeURLSchemeRegisteredAsBypassingContentSecurityPolicy(scheme);
3593}
3594
3595void Internals::registerDefaultPortForProtocol(unsigned short port, const String& protocol)
3596{
3597 registerDefaultPortForProtocolForTesting(port, protocol);
3598}
3599
3600Ref<MallocStatistics> Internals::mallocStatistics() const
3601{
3602 return MallocStatistics::create();
3603}
3604
3605Ref<TypeConversions> Internals::typeConversions() const
3606{
3607 return TypeConversions::create();
3608}
3609
3610Ref<MemoryInfo> Internals::memoryInfo() const
3611{
3612 return MemoryInfo::create();
3613}
3614
3615Vector<String> Internals::getReferencedFilePaths() const
3616{
3617 frame()->loader().history().saveDocumentAndScrollState();
3618 return FormController::referencedFilePaths(frame()->loader().history().currentItem()->documentState());
3619}
3620
3621ExceptionOr<void> Internals::startTrackingRepaints()
3622{
3623 Document* document = contextDocument();
3624 if (!document || !document->view())
3625 return Exception { InvalidAccessError };
3626
3627 document->view()->setTracksRepaints(true);
3628 return { };
3629}
3630
3631ExceptionOr<void> Internals::stopTrackingRepaints()
3632{
3633 Document* document = contextDocument();
3634 if (!document || !document->view())
3635 return Exception { InvalidAccessError };
3636
3637 document->view()->setTracksRepaints(false);
3638 return { };
3639}
3640
3641ExceptionOr<void> Internals::startTrackingLayerFlushes()
3642{
3643 Document* document = contextDocument();
3644 if (!document || !document->renderView())
3645 return Exception { InvalidAccessError };
3646
3647 document->renderView()->compositor().startTrackingLayerFlushes();
3648 return { };
3649}
3650
3651ExceptionOr<unsigned> Internals::layerFlushCount()
3652{
3653 Document* document = contextDocument();
3654 if (!document || !document->renderView())
3655 return Exception { InvalidAccessError };
3656
3657 return document->renderView()->compositor().layerFlushCount();
3658}
3659
3660ExceptionOr<void> Internals::startTrackingStyleRecalcs()
3661{
3662 Document* document = contextDocument();
3663 if (!document)
3664 return Exception { InvalidAccessError };
3665
3666 document->startTrackingStyleRecalcs();
3667 return { };
3668}
3669
3670ExceptionOr<unsigned> Internals::styleRecalcCount()
3671{
3672 Document* document = contextDocument();
3673 if (!document)
3674 return Exception { InvalidAccessError };
3675
3676 return document->styleRecalcCount();
3677}
3678
3679unsigned Internals::lastStyleUpdateSize() const
3680{
3681 Document* document = contextDocument();
3682 if (!document)
3683 return 0;
3684 return document->lastStyleUpdateSizeForTesting();
3685}
3686
3687ExceptionOr<void> Internals::startTrackingCompositingUpdates()
3688{
3689 Document* document = contextDocument();
3690 if (!document || !document->renderView())
3691 return Exception { InvalidAccessError };
3692
3693 document->renderView()->compositor().startTrackingCompositingUpdates();
3694 return { };
3695}
3696
3697ExceptionOr<unsigned> Internals::compositingUpdateCount()
3698{
3699 Document* document = contextDocument();
3700 if (!document || !document->renderView())
3701 return Exception { InvalidAccessError };
3702
3703 return document->renderView()->compositor().compositingUpdateCount();
3704}
3705
3706ExceptionOr<void> Internals::startTrackingRenderingUpdates()
3707{
3708 Document* document = contextDocument();
3709 if (!document)
3710 return Exception { InvalidAccessError };
3711
3712 document->page()->startTrackingRenderingUpdates();
3713 return { };
3714}
3715
3716ExceptionOr<unsigned> Internals::renderingUpdateCount()
3717{
3718 Document* document = contextDocument();
3719 if (!document)
3720 return Exception { InvalidAccessError };
3721
3722 return document->page()->renderingUpdateCount();
3723}
3724
3725ExceptionOr<void> Internals::setCompositingPolicyOverride(std::optional<CompositingPolicy> policyOverride)
3726{
3727 Document* document = contextDocument();
3728 if (!document)
3729 return Exception { InvalidAccessError };
3730
3731 if (!policyOverride) {
3732 document->page()->setCompositingPolicyOverride(std::nullopt);
3733 return { };
3734 }
3735
3736 switch (policyOverride.value()) {
3737 case Internals::CompositingPolicy::Normal:
3738 document->page()->setCompositingPolicyOverride(WebCore::CompositingPolicy::Normal);
3739 break;
3740 case Internals::CompositingPolicy::Conservative:
3741 document->page()->setCompositingPolicyOverride(WebCore::CompositingPolicy::Conservative);
3742 break;
3743 }
3744
3745 return { };
3746}
3747
3748ExceptionOr<std::optional<Internals::CompositingPolicy>> Internals::compositingPolicyOverride() const
3749{
3750 Document* document = contextDocument();
3751 if (!document)
3752 return Exception { InvalidAccessError };
3753
3754 auto policyOverride = document->page()->compositingPolicyOverride();
3755 if (!policyOverride)
3756 return { std::nullopt };
3757
3758 switch (policyOverride.value()) {
3759 case WebCore::CompositingPolicy::Normal:
3760 return { Internals::CompositingPolicy::Normal };
3761 case WebCore::CompositingPolicy::Conservative:
3762 return { Internals::CompositingPolicy::Conservative };
3763 }
3764
3765 return { Internals::CompositingPolicy::Normal };
3766}
3767
3768void Internals::updateLayoutAndStyleForAllFrames()
3769{
3770 auto* document = contextDocument();
3771 if (!document || !document->view())
3772 return;
3773 document->view()->updateLayoutAndStyleIfNeededRecursive();
3774}
3775
3776ExceptionOr<void> Internals::updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks(Node* node)
3777{
3778 Document* document;
3779 if (!node)
3780 document = contextDocument();
3781 else if (is<Document>(*node))
3782 document = downcast<Document>(node);
3783 else if (is<HTMLIFrameElement>(*node))
3784 document = downcast<HTMLIFrameElement>(*node).contentDocument();
3785 else
3786 return Exception { TypeError };
3787
3788 document->updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks::Synchronously);
3789 return { };
3790}
3791
3792unsigned Internals::layoutCount() const
3793{
3794 Document* document = contextDocument();
3795 if (!document || !document->view())
3796 return 0;
3797 return document->view()->layoutContext().layoutCount();
3798}
3799
3800#if !PLATFORM(IOS_FAMILY)
3801static const char* cursorTypeToString(Cursor::Type cursorType)
3802{
3803 switch (cursorType) {
3804 case Cursor::Pointer: return "Pointer";
3805 case Cursor::Cross: return "Cross";
3806 case Cursor::Hand: return "Hand";
3807 case Cursor::IBeam: return "IBeam";
3808 case Cursor::Wait: return "Wait";
3809 case Cursor::Help: return "Help";
3810 case Cursor::EastResize: return "EastResize";
3811 case Cursor::NorthResize: return "NorthResize";
3812 case Cursor::NorthEastResize: return "NorthEastResize";
3813 case Cursor::NorthWestResize: return "NorthWestResize";
3814 case Cursor::SouthResize: return "SouthResize";
3815 case Cursor::SouthEastResize: return "SouthEastResize";
3816 case Cursor::SouthWestResize: return "SouthWestResize";
3817 case Cursor::WestResize: return "WestResize";
3818 case Cursor::NorthSouthResize: return "NorthSouthResize";
3819 case Cursor::EastWestResize: return "EastWestResize";
3820 case Cursor::NorthEastSouthWestResize: return "NorthEastSouthWestResize";
3821 case Cursor::NorthWestSouthEastResize: return "NorthWestSouthEastResize";
3822 case Cursor::ColumnResize: return "ColumnResize";
3823 case Cursor::RowResize: return "RowResize";
3824 case Cursor::MiddlePanning: return "MiddlePanning";
3825 case Cursor::EastPanning: return "EastPanning";
3826 case Cursor::NorthPanning: return "NorthPanning";
3827 case Cursor::NorthEastPanning: return "NorthEastPanning";
3828 case Cursor::NorthWestPanning: return "NorthWestPanning";
3829 case Cursor::SouthPanning: return "SouthPanning";
3830 case Cursor::SouthEastPanning: return "SouthEastPanning";
3831 case Cursor::SouthWestPanning: return "SouthWestPanning";
3832 case Cursor::WestPanning: return "WestPanning";
3833 case Cursor::Move: return "Move";
3834 case Cursor::VerticalText: return "VerticalText";
3835 case Cursor::Cell: return "Cell";
3836 case Cursor::ContextMenu: return "ContextMenu";
3837 case Cursor::Alias: return "Alias";
3838 case Cursor::Progress: return "Progress";
3839 case Cursor::NoDrop: return "NoDrop";
3840 case Cursor::Copy: return "Copy";
3841 case Cursor::None: return "None";
3842 case Cursor::NotAllowed: return "NotAllowed";
3843 case Cursor::ZoomIn: return "ZoomIn";
3844 case Cursor::ZoomOut: return "ZoomOut";
3845 case Cursor::Grab: return "Grab";
3846 case Cursor::Grabbing: return "Grabbing";
3847 case Cursor::Custom: return "Custom";
3848 }
3849
3850 ASSERT_NOT_REACHED();
3851 return "UNKNOWN";
3852}
3853#endif
3854
3855ExceptionOr<String> Internals::getCurrentCursorInfo()
3856{
3857 Document* document = contextDocument();
3858 if (!document || !document->frame())
3859 return Exception { InvalidAccessError };
3860
3861#if !PLATFORM(IOS_FAMILY)
3862 Cursor cursor = document->frame()->eventHandler().currentMouseCursor();
3863
3864 StringBuilder result;
3865 result.append("type=", cursorTypeToString(cursor.type()), " hotSpot=", cursor.hotSpot().x(), ',', cursor.hotSpot().y());
3866 if (cursor.image()) {
3867 FloatSize size = cursor.image()->size();
3868 result.append(" image=", size.width(), 'x', size.height());
3869 }
3870#if ENABLE(MOUSE_CURSOR_SCALE)
3871 if (cursor.imageScaleFactor() != 1)
3872 result.append(" scale=", cursor.imageScaleFactor());
3873#endif
3874 return result.toString();
3875#else
3876 return "FAIL: Cursor details not available on this platform."_str;
3877#endif
3878}
3879
3880Ref<ArrayBuffer> Internals::serializeObject(const RefPtr<SerializedScriptValue>& value) const
3881{
3882 auto& bytes = value->wireBytes();
3883 return ArrayBuffer::create(bytes.data(), bytes.size());
3884}
3885
3886Ref<SerializedScriptValue> Internals::deserializeBuffer(ArrayBuffer& buffer) const
3887{
3888 Vector<uint8_t> bytes { static_cast<const uint8_t*>(buffer.data()), buffer.byteLength() };
3889 return SerializedScriptValue::createFromWireBytes(WTFMove(bytes));
3890}
3891
3892bool Internals::isFromCurrentWorld(JSC::JSValue value) const
3893{
3894 JSC::VM& vm = contextDocument()->vm();
3895 return isWorldCompatible(*vm.topCallFrame->lexicalGlobalObject(vm), value);
3896}
3897
3898JSC::JSValue Internals::evaluateInWorldIgnoringException(const String& name, const String& source)
3899{
3900 auto* document = contextDocument();
3901 auto& scriptController = document->frame()->script();
3902 auto world = ScriptController::createWorld(name);
3903 return scriptController.executeScriptInWorldIgnoringException(world, source);
3904}
3905
3906void Internals::setUsesOverlayScrollbars(bool enabled)
3907{
3908 WebCore::DeprecatedGlobalSettings::setUsesOverlayScrollbars(enabled);
3909#if PLATFORM(MAC)
3910 ScrollerStyle::setUseOverlayScrollbars(enabled);
3911 ScrollbarTheme& theme = ScrollbarTheme::theme();
3912 if (theme.isMockTheme())
3913 return;
3914
3915 static_cast<ScrollbarThemeMac&>(theme).preferencesChanged();
3916#endif
3917}
3918
3919void Internals::forceReload(bool endToEnd)
3920{
3921 OptionSet<ReloadOption> reloadOptions;
3922 if (endToEnd)
3923 reloadOptions.add(ReloadOption::FromOrigin);
3924
3925 frame()->loader().reload(reloadOptions);
3926}
3927
3928void Internals::reloadExpiredOnly()
3929{
3930 frame()->loader().reload(ReloadOption::ExpiredOnly);
3931}
3932
3933void Internals::enableFixedWidthAutoSizeMode(bool enabled, int width, int height)
3934{
3935 auto* document = contextDocument();
3936 if (!document || !document->view())
3937 return;
3938 document->view()->enableFixedWidthAutoSizeMode(enabled, { width, height });
3939}
3940
3941void Internals::enableSizeToContentAutoSizeMode(bool enabled, int width, int height)
3942{
3943 auto* document = contextDocument();
3944 if (!document || !document->view())
3945 return;
3946 document->view()->enableSizeToContentAutoSizeMode(enabled, { width, height });
3947}
3948
3949#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
3950
3951void Internals::initializeMockCDM()
3952{
3953 LegacyCDM::registerCDMFactory([] (LegacyCDM* cdm) { return makeUnique<LegacyMockCDM>(cdm); },
3954 LegacyMockCDM::supportsKeySystem, LegacyMockCDM::supportsKeySystemAndMimeType);
3955}
3956
3957#endif
3958
3959#if ENABLE(ENCRYPTED_MEDIA)
3960
3961Ref<MockCDMFactory> Internals::registerMockCDM()
3962{
3963 return MockCDMFactory::create();
3964}
3965
3966#endif
3967
3968String Internals::markerTextForListItem(Element& element)
3969{
3970 return WebCore::markerTextForListItem(&element);
3971}
3972
3973String Internals::toolTipFromElement(Element& element) const
3974{
3975 HitTestResult result;
3976 result.setInnerNode(&element);
3977 TextDirection direction;
3978 return result.title(direction);
3979}
3980
3981String Internals::getImageSourceURL(Element& element)
3982{
3983 return element.imageSourceURL();
3984}
3985
3986#if ENABLE(VIDEO)
3987
3988unsigned Internals::mediaElementCount()
3989{
3990 Document* document = contextDocument();
3991 if (!document)
3992 return 0;
3993
3994 unsigned number = 0;
3995 for (auto* mediaElement : HTMLMediaElement::allMediaElements()) {
3996 if (&mediaElement->document() == document)
3997 ++number;
3998 }
3999
4000 return number;
4001}
4002
4003Vector<String> Internals::mediaResponseSources(HTMLMediaElement& media)
4004{
4005 auto* resourceLoader = media.lastMediaResourceLoaderForTesting();
4006 if (!resourceLoader)
4007 return { };
4008 Vector<String> result;
4009 auto responses = resourceLoader->responsesForTesting();
4010 for (auto& response : responses)
4011 result.append(responseSourceToString(response));
4012 return result;
4013}
4014
4015Vector<String> Internals::mediaResponseContentRanges(HTMLMediaElement& media)
4016{
4017 auto* resourceLoader = media.lastMediaResourceLoaderForTesting();
4018 if (!resourceLoader)
4019 return { };
4020 Vector<String> result;
4021 auto responses = resourceLoader->responsesForTesting();
4022 for (auto& response : responses)
4023 result.append(response.httpHeaderField(HTTPHeaderName::ContentRange));
4024 return result;
4025}
4026
4027void Internals::simulateAudioInterruption(HTMLMediaElement& element)
4028{
4029#if USE(GSTREAMER)
4030 element.player()->simulateAudioInterruption();
4031#else
4032 UNUSED_PARAM(element);
4033#endif
4034}
4035
4036ExceptionOr<bool> Internals::mediaElementHasCharacteristic(HTMLMediaElement& element, const String& characteristic)
4037{
4038 if (equalLettersIgnoringASCIICase(characteristic, "audible"_s))
4039 return element.hasAudio();
4040 if (equalLettersIgnoringASCIICase(characteristic, "visual"_s))
4041 return element.hasVideo();
4042 if (equalLettersIgnoringASCIICase(characteristic, "legible"_s))
4043 return element.hasClosedCaptions();
4044
4045 return Exception { SyntaxError };
4046}
4047
4048void Internals::beginSimulatedHDCPError(HTMLMediaElement& element)
4049{
4050 if (auto player = element.player())
4051 player->beginSimulatedHDCPError();
4052}
4053
4054void Internals::endSimulatedHDCPError(HTMLMediaElement& element)
4055{
4056 if (auto player = element.player())
4057 player->endSimulatedHDCPError();
4058}
4059
4060ExceptionOr<bool> Internals::mediaPlayerRenderingCanBeAccelerated(HTMLMediaElement& element)
4061{
4062 return element.mediaPlayerRenderingCanBeAccelerated();
4063}
4064
4065bool Internals::elementShouldBufferData(HTMLMediaElement& element)
4066{
4067 return element.bufferingPolicy() < MediaPlayer::BufferingPolicy::LimitReadAhead;
4068}
4069
4070String Internals::elementBufferingPolicy(HTMLMediaElement& element)
4071{
4072 switch (element.bufferingPolicy()) {
4073 case MediaPlayer::BufferingPolicy::Default:
4074 return "Default"_s;
4075 case MediaPlayer::BufferingPolicy::LimitReadAhead:
4076 return "LimitReadAhead"_s;
4077 case MediaPlayer::BufferingPolicy::MakeResourcesPurgeable:
4078 return "MakeResourcesPurgeable"_s;
4079 case MediaPlayer::BufferingPolicy::PurgeResources:
4080 return "PurgeResources"_s;
4081 }
4082
4083 ASSERT_NOT_REACHED();
4084 return "UNKNOWN"_s;
4085}
4086
4087ExceptionOr<void> Internals::setOverridePreferredDynamicRangeMode(HTMLMediaElement& element, const String& modeString)
4088{
4089 DynamicRangeMode mode;
4090 if (modeString == "None"_s)
4091 mode = DynamicRangeMode::None;
4092 else if (modeString == "Standard"_s)
4093 mode = DynamicRangeMode::Standard;
4094 else if (modeString == "HLG"_s)
4095 mode = DynamicRangeMode::HLG;
4096 else if (modeString == "HDR10"_s)
4097 mode = DynamicRangeMode::HDR10;
4098 else if (modeString == "DolbyVisionPQ"_s)
4099 mode = DynamicRangeMode::DolbyVisionPQ;
4100 else
4101 return Exception { SyntaxError };
4102
4103 element.setOverridePreferredDynamicRangeMode(mode);
4104 return { };
4105}
4106
4107#endif
4108
4109bool Internals::isSelectPopupVisible(HTMLSelectElement& element)
4110{
4111 element.document().updateLayoutIgnorePendingStylesheets();
4112
4113 auto* renderer = element.renderer();
4114 if (!is<RenderMenuList>(renderer))
4115 return false;
4116
4117#if !PLATFORM(IOS_FAMILY)
4118 return downcast<RenderMenuList>(*renderer).popupIsVisible();
4119#else
4120 return false;
4121#endif
4122}
4123
4124ExceptionOr<String> Internals::captionsStyleSheetOverride()
4125{
4126 Document* document = contextDocument();
4127 if (!document || !document->page())
4128 return Exception { InvalidAccessError };
4129
4130#if ENABLE(VIDEO)
4131 return document->page()->group().ensureCaptionPreferences().captionsStyleSheetOverride();
4132#else
4133 return String { emptyString() };
4134#endif
4135}
4136
4137ExceptionOr<void> Internals::setCaptionsStyleSheetOverride(const String& override)
4138{
4139 Document* document = contextDocument();
4140 if (!document || !document->page())
4141 return Exception { InvalidAccessError };
4142
4143#if ENABLE(VIDEO)
4144 document->page()->group().ensureCaptionPreferences().setCaptionsStyleSheetOverride(override);
4145#else
4146 UNUSED_PARAM(override);
4147#endif
4148 return { };
4149}
4150
4151ExceptionOr<void> Internals::setPrimaryAudioTrackLanguageOverride(const String& language)
4152{
4153 Document* document = contextDocument();
4154 if (!document || !document->page())
4155 return Exception { InvalidAccessError };
4156
4157#if ENABLE(VIDEO)
4158 document->page()->group().ensureCaptionPreferences().setPrimaryAudioTrackLanguageOverride(language);
4159#else
4160 UNUSED_PARAM(language);
4161#endif
4162 return { };
4163}
4164
4165ExceptionOr<void> Internals::setCaptionDisplayMode(const String& mode)
4166{
4167 Document* document = contextDocument();
4168 if (!document || !document->page())
4169 return Exception { InvalidAccessError };
4170
4171#if ENABLE(VIDEO)
4172 auto& captionPreferences = document->page()->group().ensureCaptionPreferences();
4173
4174 if (equalLettersIgnoringASCIICase(mode, "automatic"_s))
4175 captionPreferences.setCaptionDisplayMode(CaptionUserPreferences::Automatic);
4176 else if (equalLettersIgnoringASCIICase(mode, "forcedonly"_s))
4177 captionPreferences.setCaptionDisplayMode(CaptionUserPreferences::ForcedOnly);
4178 else if (equalLettersIgnoringASCIICase(mode, "alwayson"_s))
4179 captionPreferences.setCaptionDisplayMode(CaptionUserPreferences::AlwaysOn);
4180 else if (equalLettersIgnoringASCIICase(mode, "manual"_s))
4181 captionPreferences.setCaptionDisplayMode(CaptionUserPreferences::Manual);
4182 else
4183 return Exception { SyntaxError };
4184#else
4185 UNUSED_PARAM(mode);
4186#endif
4187 return { };
4188}
4189
4190#if ENABLE(VIDEO)
4191RefPtr<TextTrackCueGeneric> Internals::createGenericCue(double startTime, double endTime, String text)
4192{
4193 Document* document = contextDocument();
4194 if (!document || !document->page())
4195 return nullptr;
4196 return TextTrackCueGeneric::create(*document, MediaTime::createWithDouble(startTime), MediaTime::createWithDouble(endTime), text);
4197}
4198
4199ExceptionOr<String> Internals::textTrackBCP47Language(TextTrack& track)
4200{
4201 return String { track.validBCP47Language() };
4202}
4203
4204Ref<TimeRanges> Internals::createTimeRanges(Float32Array& startTimes, Float32Array& endTimes)
4205{
4206 ASSERT(startTimes.length() == endTimes.length());
4207 Ref<TimeRanges> ranges = TimeRanges::create();
4208
4209 unsigned count = std::min(startTimes.length(), endTimes.length());
4210 for (unsigned i = 0; i < count; ++i)
4211 ranges->add(startTimes.item(i), endTimes.item(i));
4212 return ranges;
4213}
4214
4215double Internals::closestTimeToTimeRanges(double time, TimeRanges& ranges)
4216{
4217 return ranges.nearest(time);
4218}
4219
4220#endif
4221
4222ExceptionOr<Ref<DOMRect>> Internals::selectionBounds()
4223{
4224 Document* document = contextDocument();
4225 if (!document || !document->frame())
4226 return Exception { InvalidAccessError };
4227
4228 return DOMRect::create(document->frame()->selection().selectionBounds());
4229}
4230
4231void Internals::setSelectionWithoutValidation(Ref<Node> baseNode, unsigned baseOffset, RefPtr<Node> extentNode, unsigned extentOffset)
4232{
4233 contextDocument()->frame()->selection().moveTo(
4234 VisiblePosition { makeDeprecatedLegacyPosition(baseNode.ptr(), baseOffset) },
4235 VisiblePosition { makeDeprecatedLegacyPosition(extentNode.get(), extentOffset) });
4236}
4237
4238ExceptionOr<bool> Internals::isPluginUnavailabilityIndicatorObscured(Element& element)
4239{
4240 if (!is<HTMLPlugInElement>(element))
4241 return Exception { InvalidAccessError };
4242
4243 return downcast<HTMLPlugInElement>(element).isReplacementObscured();
4244}
4245
4246ExceptionOr<String> Internals::unavailablePluginReplacementText(Element& element)
4247{
4248 if (!is<HTMLPlugInElement>(element))
4249 return Exception { InvalidAccessError };
4250
4251 auto* renderer = element.renderer();
4252 if (!is<RenderEmbeddedObject>(renderer))
4253 return String { };
4254
4255 return String { downcast<RenderEmbeddedObject>(*renderer).pluginReplacementTextIfUnavailable() };
4256}
4257
4258bool Internals::isPluginSnapshotted(Element&)
4259{
4260 return false;
4261}
4262
4263#if ENABLE(MEDIA_SOURCE)
4264
4265void Internals::initializeMockMediaSource()
4266{
4267#if USE(AVFOUNDATION)
4268 WebCore::DeprecatedGlobalSettings::setAVFoundationEnabled(false);
4269#endif
4270#if USE(GSTREAMER)
4271 WebCore::DeprecatedGlobalSettings::setGStreamerEnabled(false);
4272#endif
4273 MediaPlayerFactorySupport::callRegisterMediaEngine(MockMediaPlayerMediaSource::registerMediaEngine);
4274}
4275
4276void Internals::bufferedSamplesForTrackId(SourceBuffer& buffer, const AtomString& trackId, BufferedSamplesPromise&& promise)
4277{
4278 buffer.bufferedSamplesForTrackId(trackId, [promise = WTFMove(promise)](auto&& samples) mutable {
4279 promise.resolve(WTFMove(samples));
4280 });
4281}
4282
4283void Internals::enqueuedSamplesForTrackID(SourceBuffer& buffer, const AtomString& trackID, BufferedSamplesPromise&& promise)
4284{
4285 return buffer.enqueuedSamplesForTrackID(trackID, [promise = WTFMove(promise)](auto&& samples) mutable {
4286 promise.resolve(WTFMove(samples));
4287 });
4288}
4289
4290double Internals::minimumUpcomingPresentationTimeForTrackID(SourceBuffer& buffer, const AtomString& trackID)
4291{
4292 return buffer.minimumUpcomingPresentationTimeForTrackID(trackID).toDouble();
4293}
4294
4295void Internals::setShouldGenerateTimestamps(SourceBuffer& buffer, bool flag)
4296{
4297 buffer.setShouldGenerateTimestamps(flag);
4298}
4299
4300void Internals::setMaximumQueueDepthForTrackID(SourceBuffer& buffer, const AtomString& trackID, size_t maxQueueDepth)
4301{
4302 buffer.setMaximumQueueDepthForTrackID(trackID, maxQueueDepth);
4303}
4304
4305#endif
4306
4307void Internals::enableMockMediaCapabilities()
4308{
4309 MediaEngineConfigurationFactory::enableMock();
4310}
4311
4312#if ENABLE(VIDEO)
4313
4314ExceptionOr<void> Internals::beginMediaSessionInterruption(const String& interruptionString)
4315{
4316 PlatformMediaSession::InterruptionType interruption = PlatformMediaSession::SystemInterruption;
4317
4318 if (equalLettersIgnoringASCIICase(interruptionString, "system"_s))
4319 interruption = PlatformMediaSession::SystemInterruption;
4320 else if (equalLettersIgnoringASCIICase(interruptionString, "systemsleep"_s))
4321 interruption = PlatformMediaSession::SystemSleep;
4322 else if (equalLettersIgnoringASCIICase(interruptionString, "enteringbackground"_s))
4323 interruption = PlatformMediaSession::EnteringBackground;
4324 else if (equalLettersIgnoringASCIICase(interruptionString, "suspendedunderlock"_s))
4325 interruption = PlatformMediaSession::SuspendedUnderLock;
4326 else
4327 return Exception { InvalidAccessError };
4328
4329 PlatformMediaSessionManager::sharedManager().beginInterruption(interruption);
4330 return { };
4331}
4332
4333void Internals::endMediaSessionInterruption(const String& flagsString)
4334{
4335 PlatformMediaSession::EndInterruptionFlags flags = PlatformMediaSession::NoFlags;
4336
4337 if (equalLettersIgnoringASCIICase(flagsString, "mayresumeplaying"_s))
4338 flags = PlatformMediaSession::MayResumePlaying;
4339
4340 PlatformMediaSessionManager::sharedManager().endInterruption(flags);
4341}
4342
4343void Internals::applicationWillBecomeInactive()
4344{
4345 PlatformMediaSessionManager::sharedManager().applicationWillBecomeInactive();
4346}
4347
4348void Internals::applicationDidBecomeActive()
4349{
4350 PlatformMediaSessionManager::sharedManager().applicationDidBecomeActive();
4351}
4352
4353void Internals::applicationWillEnterForeground(bool suspendedUnderLock) const
4354{
4355 PlatformMediaSessionManager::sharedManager().applicationWillEnterForeground(suspendedUnderLock);
4356}
4357
4358void Internals::applicationDidEnterBackground(bool suspendedUnderLock) const
4359{
4360 PlatformMediaSessionManager::sharedManager().applicationDidEnterBackground(suspendedUnderLock);
4361}
4362
4363static PlatformMediaSession::MediaType mediaTypeFromString(const String& mediaTypeString)
4364{
4365 if (equalLettersIgnoringASCIICase(mediaTypeString, "video"_s))
4366 return PlatformMediaSession::MediaType::Video;
4367 if (equalLettersIgnoringASCIICase(mediaTypeString, "audio"_s))
4368 return PlatformMediaSession::MediaType::Audio;
4369 if (equalLettersIgnoringASCIICase(mediaTypeString, "videoaudio"_s))
4370 return PlatformMediaSession::MediaType::VideoAudio;
4371 if (equalLettersIgnoringASCIICase(mediaTypeString, "webaudio"_s))
4372 return PlatformMediaSession::MediaType::WebAudio;
4373
4374 return PlatformMediaSession::MediaType::None;
4375}
4376
4377ExceptionOr<void> Internals::setMediaSessionRestrictions(const String& mediaTypeString, StringView restrictionsString)
4378{
4379 auto mediaType = mediaTypeFromString(mediaTypeString);
4380 if (mediaType == PlatformMediaSession::MediaType::None)
4381 return Exception { InvalidAccessError };
4382
4383 auto restrictions = PlatformMediaSessionManager::sharedManager().restrictions(mediaType);
4384 PlatformMediaSessionManager::sharedManager().removeRestriction(mediaType, restrictions);
4385
4386 restrictions = PlatformMediaSessionManager::NoRestrictions;
4387
4388 for (StringView restrictionString : restrictionsString.split(',')) {
4389 if (equalLettersIgnoringASCIICase(restrictionString, "concurrentplaybacknotpermitted"_s))
4390 restrictions |= PlatformMediaSessionManager::ConcurrentPlaybackNotPermitted;
4391 if (equalLettersIgnoringASCIICase(restrictionString, "backgroundprocessplaybackrestricted"_s))
4392 restrictions |= PlatformMediaSessionManager::BackgroundProcessPlaybackRestricted;
4393 if (equalLettersIgnoringASCIICase(restrictionString, "backgroundtabplaybackrestricted"_s))
4394 restrictions |= PlatformMediaSessionManager::BackgroundTabPlaybackRestricted;
4395 if (equalLettersIgnoringASCIICase(restrictionString, "interruptedplaybacknotpermitted"_s))
4396 restrictions |= PlatformMediaSessionManager::InterruptedPlaybackNotPermitted;
4397 if (equalLettersIgnoringASCIICase(restrictionString, "inactiveprocessplaybackrestricted"_s))
4398 restrictions |= PlatformMediaSessionManager::InactiveProcessPlaybackRestricted;
4399 if (equalLettersIgnoringASCIICase(restrictionString, "suspendedunderlockplaybackrestricted"_s))
4400 restrictions |= PlatformMediaSessionManager::SuspendedUnderLockPlaybackRestricted;
4401 }
4402 PlatformMediaSessionManager::sharedManager().addRestriction(mediaType, restrictions);
4403 return { };
4404}
4405
4406ExceptionOr<String> Internals::mediaSessionRestrictions(const String& mediaTypeString) const
4407{
4408 PlatformMediaSession::MediaType mediaType = mediaTypeFromString(mediaTypeString);
4409 if (mediaType == PlatformMediaSession::MediaType::None)
4410 return Exception { InvalidAccessError };
4411
4412 PlatformMediaSessionManager::SessionRestrictions restrictions = PlatformMediaSessionManager::sharedManager().restrictions(mediaType);
4413 if (restrictions == PlatformMediaSessionManager::NoRestrictions)
4414 return String();
4415
4416 StringBuilder builder;
4417 if (restrictions & PlatformMediaSessionManager::ConcurrentPlaybackNotPermitted)
4418 builder.append("concurrentplaybacknotpermitted");
4419 if (restrictions & PlatformMediaSessionManager::BackgroundProcessPlaybackRestricted) {
4420 if (!builder.isEmpty())
4421 builder.append(',');
4422 builder.append("backgroundprocessplaybackrestricted");
4423 }
4424 if (restrictions & PlatformMediaSessionManager::BackgroundTabPlaybackRestricted) {
4425 if (!builder.isEmpty())
4426 builder.append(',');
4427 builder.append("backgroundtabplaybackrestricted");
4428 }
4429 if (restrictions & PlatformMediaSessionManager::InterruptedPlaybackNotPermitted) {
4430 if (!builder.isEmpty())
4431 builder.append(',');
4432 builder.append("interruptedplaybacknotpermitted");
4433 }
4434 return builder.toString();
4435}
4436
4437void Internals::setMediaElementRestrictions(HTMLMediaElement& element, StringView restrictionsString)
4438{
4439 MediaElementSession::BehaviorRestrictions restrictions = element.mediaSession().behaviorRestrictions();
4440 element.mediaSession().removeBehaviorRestriction(restrictions);
4441
4442 restrictions = MediaElementSession::NoRestrictions;
4443
4444 for (StringView restrictionString : restrictionsString.split(',')) {
4445 if (equalLettersIgnoringASCIICase(restrictionString, "norestrictions"_s))
4446 restrictions |= MediaElementSession::NoRestrictions;
4447 if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforload"_s))
4448 restrictions |= MediaElementSession::RequireUserGestureForLoad;
4449 if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforvideoratechange"_s))
4450 restrictions |= MediaElementSession::RequireUserGestureForVideoRateChange;
4451 if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforfullscreen"_s))
4452 restrictions |= MediaElementSession::RequireUserGestureForFullscreen;
4453 if (equalLettersIgnoringASCIICase(restrictionString, "requirepageconsenttoloadmedia"_s))
4454 restrictions |= MediaElementSession::RequirePageConsentToLoadMedia;
4455 if (equalLettersIgnoringASCIICase(restrictionString, "requirepageconsenttoresumemedia"_s))
4456 restrictions |= MediaElementSession::RequirePageConsentToResumeMedia;
4457#if ENABLE(WIRELESS_PLAYBACK_TARGET)
4458 if (equalLettersIgnoringASCIICase(restrictionString, "requireusergesturetoshowplaybacktargetpicker"_s))
4459 restrictions |= MediaElementSession::RequireUserGestureToShowPlaybackTargetPicker;
4460 if (equalLettersIgnoringASCIICase(restrictionString, "wirelessvideoplaybackdisabled"_s))
4461 restrictions |= MediaElementSession::WirelessVideoPlaybackDisabled;
4462#endif
4463 if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforaudioratechange"_s))
4464 restrictions |= MediaElementSession::RequireUserGestureForAudioRateChange;
4465 if (equalLettersIgnoringASCIICase(restrictionString, "autopreloadingnotpermitted"_s))
4466 restrictions |= MediaElementSession::AutoPreloadingNotPermitted;
4467 if (equalLettersIgnoringASCIICase(restrictionString, "invisibleautoplaynotpermitted"_s))
4468 restrictions |= MediaElementSession::InvisibleAutoplayNotPermitted;
4469 if (equalLettersIgnoringASCIICase(restrictionString, "overrideusergesturerequirementformaincontent"_s))
4470 restrictions |= MediaElementSession::OverrideUserGestureRequirementForMainContent;
4471 if (equalLettersIgnoringASCIICase(restrictionString, "requireusergesturetocontrolcontrolsmanager"_s))
4472 restrictions |= MediaElementSession::RequireUserGestureToControlControlsManager;
4473 if (equalLettersIgnoringASCIICase(restrictionString, "requireplaybackTocontrolcontrolsmanager"_s))
4474 restrictions |= MediaElementSession::RequirePlaybackToControlControlsManager;
4475 if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforvideoduetolowpowermode"_s))
4476 restrictions |= MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode;
4477 if (equalLettersIgnoringASCIICase(restrictionString, "requirepagevisibilitytoplayaudio"_s))
4478 restrictions |= MediaElementSession::RequirePageVisibilityToPlayAudio;
4479 }
4480 element.mediaSession().addBehaviorRestriction(restrictions);
4481}
4482
4483ExceptionOr<void> Internals::postRemoteControlCommand(const String& commandString, float argument)
4484{
4485 PlatformMediaSession::RemoteControlCommandType command;
4486 PlatformMediaSession::RemoteCommandArgument parameter { argument, { } };
4487
4488 if (equalLettersIgnoringASCIICase(commandString, "play"_s))
4489 command = PlatformMediaSession::PlayCommand;
4490 else if (equalLettersIgnoringASCIICase(commandString, "pause"_s))
4491 command = PlatformMediaSession::PauseCommand;
4492 else if (equalLettersIgnoringASCIICase(commandString, "stop"_s))
4493 command = PlatformMediaSession::StopCommand;
4494 else if (equalLettersIgnoringASCIICase(commandString, "toggleplaypause"_s))
4495 command = PlatformMediaSession::TogglePlayPauseCommand;
4496 else if (equalLettersIgnoringASCIICase(commandString, "beginseekingbackward"_s))
4497 command = PlatformMediaSession::BeginSeekingBackwardCommand;
4498 else if (equalLettersIgnoringASCIICase(commandString, "endseekingbackward"_s))
4499 command = PlatformMediaSession::EndSeekingBackwardCommand;
4500 else if (equalLettersIgnoringASCIICase(commandString, "beginseekingforward"_s))
4501 command = PlatformMediaSession::BeginSeekingForwardCommand;
4502 else if (equalLettersIgnoringASCIICase(commandString, "endseekingforward"_s))
4503 command = PlatformMediaSession::EndSeekingForwardCommand;
4504 else if (equalLettersIgnoringASCIICase(commandString, "seektoplaybackposition"_s))
4505 command = PlatformMediaSession::SeekToPlaybackPositionCommand;
4506 else if (equalLettersIgnoringASCIICase(commandString, "beginscrubbing"_s))
4507 command = PlatformMediaSession::BeginScrubbingCommand;
4508 else if (equalLettersIgnoringASCIICase(commandString, "endscrubbing"_s))
4509 command = PlatformMediaSession::EndScrubbingCommand;
4510 else
4511 return Exception { InvalidAccessError };
4512
4513 PlatformMediaSessionManager::sharedManager().processDidReceiveRemoteControlCommand(command, parameter);
4514 return { };
4515}
4516
4517void Internals::activeAudioRouteDidChange(bool shouldPause)
4518{
4519#if PLATFORM(IOS)
4520 MediaSessionHelper::sharedHelper().activeAudioRouteDidChange(shouldPause ? MediaSessionHelperClient::ShouldPause::Yes : MediaSessionHelperClient::ShouldPause::No);
4521#else
4522 UNUSED_PARAM(shouldPause);
4523#endif
4524}
4525
4526bool Internals::elementIsBlockingDisplaySleep(const HTMLMediaElement& element) const
4527{
4528 return element.isDisablingSleep();
4529}
4530
4531bool Internals::isPlayerVisibleInViewport(const HTMLMediaElement& element) const
4532{
4533 auto player = element.player();
4534 return player && player->isVisibleInViewport();
4535}
4536
4537bool Internals::isPlayerMuted(const HTMLMediaElement& element) const
4538{
4539 auto player = element.player();
4540 return player && player->muted();
4541}
4542
4543void Internals::beginAudioSessionInterruption()
4544{
4545#if USE(AUDIO_SESSION)
4546 AudioSession::sharedSession().beginInterruption();
4547#endif
4548}
4549#endif // ENABLE(VIDEO)
4550
4551#if ENABLE(WEB_AUDIO)
4552void Internals::setAudioContextRestrictions(AudioContext& context, StringView restrictionsString)
4553{
4554 auto restrictions = context.behaviorRestrictions();
4555 context.removeBehaviorRestriction(restrictions);
4556
4557 restrictions = AudioContext::NoRestrictions;
4558
4559 for (StringView restrictionString : restrictionsString.split(',')) {
4560 if (equalLettersIgnoringASCIICase(restrictionString, "norestrictions"_s))
4561 restrictions |= AudioContext::NoRestrictions;
4562 if (equalLettersIgnoringASCIICase(restrictionString, "requireusergestureforaudiostart"_s))
4563 restrictions |= AudioContext::RequireUserGestureForAudioStartRestriction;
4564 if (equalLettersIgnoringASCIICase(restrictionString, "requirepageconsentforaudiostart"_s))
4565 restrictions |= AudioContext::RequirePageConsentForAudioStartRestriction;
4566 }
4567 context.addBehaviorRestriction(restrictions);
4568}
4569
4570void Internals::useMockAudioDestinationCocoa()
4571{
4572#if PLATFORM(COCOA)
4573 AudioDestinationCocoa::createOverride = MockAudioDestinationCocoa::create;
4574#endif
4575}
4576#endif
4577
4578void Internals::simulateSystemSleep() const
4579{
4580#if ENABLE(VIDEO)
4581 PlatformMediaSessionManager::sharedManager().processSystemWillSleep();
4582#endif
4583}
4584
4585void Internals::simulateSystemWake() const
4586{
4587#if ENABLE(VIDEO)
4588 PlatformMediaSessionManager::sharedManager().processSystemDidWake();
4589#endif
4590}
4591
4592ExceptionOr<Internals::NowPlayingState> Internals::nowPlayingState() const
4593{
4594#if ENABLE(VIDEO)
4595 return { { PlatformMediaSessionManager::sharedManager().lastUpdatedNowPlayingTitle(),
4596 PlatformMediaSessionManager::sharedManager().lastUpdatedNowPlayingDuration(),
4597 PlatformMediaSessionManager::sharedManager().lastUpdatedNowPlayingElapsedTime(),
4598 PlatformMediaSessionManager::sharedManager().lastUpdatedNowPlayingInfoUniqueIdentifier().toUInt64(),
4599 PlatformMediaSessionManager::sharedManager().hasActiveNowPlayingSession(),
4600 PlatformMediaSessionManager::sharedManager().registeredAsNowPlayingApplication(),
4601 PlatformMediaSessionManager::sharedManager().haveEverRegisteredAsNowPlayingApplication()
4602 } };
4603#else
4604 return Exception { InvalidAccessError };
4605#endif
4606}
4607
4608#if ENABLE(VIDEO)
4609RefPtr<HTMLMediaElement> Internals::bestMediaElementForRemoteControls(Internals::PlaybackControlsPurpose purpose)
4610{
4611 return HTMLMediaElement::bestMediaElementForRemoteControls(purpose);
4612}
4613
4614Internals::MediaSessionState Internals::mediaSessionState(HTMLMediaElement& element)
4615{
4616 return static_cast<Internals::MediaSessionState>(element.mediaSession().state());
4617}
4618#endif
4619
4620ExceptionOr<Internals::MediaUsageState> Internals::mediaUsageState(HTMLMediaElement& element) const
4621{
4622#if ENABLE(VIDEO)
4623 element.mediaSession().updateMediaUsageIfChanged();
4624 auto info = element.mediaSession().mediaUsageInfo();
4625 if (!info)
4626 return Exception { NotSupportedError };
4627
4628 return { { info.value().mediaURL.string(),
4629 info.value().isPlaying,
4630 info.value().canShowControlsManager,
4631 info.value().canShowNowPlayingControls,
4632 info.value().isSuspended,
4633 info.value().isInActiveDocument,
4634 info.value().isFullscreen,
4635 info.value().isMuted,
4636 info.value().isMediaDocumentInMainFrame,
4637 info.value().isVideo,
4638 info.value().isAudio,
4639 info.value().hasVideo,
4640 info.value().hasAudio,
4641 info.value().hasRenderer,
4642 info.value().audioElementWithUserGesture,
4643 info.value().userHasPlayedAudioBefore,
4644 info.value().isElementRectMostlyInMainFrame,
4645 info.value().playbackPermitted,
4646 info.value().pageMediaPlaybackSuspended,
4647 info.value().isMediaDocumentAndNotOwnerElement,
4648 info.value().pageExplicitlyAllowsElementToAutoplayInline,
4649 info.value().requiresFullscreenForVideoPlaybackAndFullscreenNotPermitted,
4650 info.value().hasHadUserInteractionAndQuirksContainsShouldAutoplayForArbitraryUserGesture,
4651 info.value().isVideoAndRequiresUserGestureForVideoRateChange,
4652 info.value().isAudioAndRequiresUserGestureForAudioRateChange,
4653 info.value().isVideoAndRequiresUserGestureForVideoDueToLowPowerMode,
4654 info.value().noUserGestureRequired,
4655 info.value().requiresPlaybackAndIsNotPlaying,
4656 info.value().hasEverNotifiedAboutPlaying,
4657 info.value().outsideOfFullscreen,
4658 info.value().isLargeEnoughForMainContent,
4659 } };
4660
4661#else
4662 UNUSED_PARAM(element);
4663 return Exception { InvalidAccessError };
4664#endif
4665}
4666
4667ExceptionOr<bool> Internals::elementShouldDisplayPosterImage(HTMLVideoElement& element) const
4668{
4669#if ENABLE(VIDEO)
4670 return element.shouldDisplayPosterImage();
4671#else
4672 UNUSED_PARAM(element);
4673 return Exception { InvalidAccessError };
4674#endif
4675}
4676
4677#if ENABLE(VIDEO)
4678size_t Internals::mediaElementCount() const
4679{
4680 return HTMLMediaElement::allMediaElements().size();
4681}
4682
4683void Internals::setMediaElementVolumeLocked(HTMLMediaElement& element, bool volumeLocked)
4684{
4685 element.setVolumeLocked(volumeLocked);
4686}
4687#endif
4688
4689#if ENABLE(WIRELESS_PLAYBACK_TARGET)
4690
4691void Internals::setMockMediaPlaybackTargetPickerEnabled(bool enabled)
4692{
4693 Page* page = contextDocument()->frame()->page();
4694 ASSERT(page);
4695
4696 page->setMockMediaPlaybackTargetPickerEnabled(enabled);
4697}
4698
4699ExceptionOr<void> Internals::setMockMediaPlaybackTargetPickerState(const String& deviceName, const String& deviceState)
4700{
4701 Page* page = contextDocument()->frame()->page();
4702 ASSERT(page);
4703
4704 MediaPlaybackTargetContext::MockState state = MediaPlaybackTargetContext::MockState::Unknown;
4705
4706 if (equalLettersIgnoringASCIICase(deviceState, "deviceavailable"_s))
4707 state = MediaPlaybackTargetContext::MockState::OutputDeviceAvailable;
4708 else if (equalLettersIgnoringASCIICase(deviceState, "deviceunavailable"_s))
4709 state = MediaPlaybackTargetContext::MockState::OutputDeviceUnavailable;
4710 else if (equalLettersIgnoringASCIICase(deviceState, "unknown"_s))
4711 state = MediaPlaybackTargetContext::MockState::Unknown;
4712 else
4713 return Exception { InvalidAccessError };
4714
4715 page->setMockMediaPlaybackTargetPickerState(deviceName, state);
4716 return { };
4717}
4718
4719void Internals::mockMediaPlaybackTargetPickerDismissPopup()
4720{
4721 auto* page = contextDocument()->frame()->page();
4722 ASSERT(page);
4723
4724 page->mockMediaPlaybackTargetPickerDismissPopup();
4725}
4726
4727#endif
4728
4729ExceptionOr<Ref<MockPageOverlay>> Internals::installMockPageOverlay(PageOverlayType type)
4730{
4731 Document* document = contextDocument();
4732 if (!document || !document->page())
4733 return Exception { InvalidAccessError };
4734
4735 return MockPageOverlayClient::singleton().installOverlay(*document->page(), type == PageOverlayType::View ? PageOverlay::OverlayType::View : PageOverlay::OverlayType::Document);
4736}
4737
4738ExceptionOr<String> Internals::pageOverlayLayerTreeAsText(unsigned short flags) const
4739{
4740 Document* document = contextDocument();
4741 if (!document || !document->page())
4742 return Exception { InvalidAccessError };
4743
4744 document->updateLayoutIgnorePendingStylesheets();
4745
4746 return MockPageOverlayClient::singleton().layerTreeAsText(*document->page(), toLayerTreeAsTextOptions(flags));
4747}
4748
4749void Internals::setPageMuted(StringView statesString)
4750{
4751 Document* document = contextDocument();
4752 if (!document)
4753 return;
4754
4755 WebCore::MediaProducerMutedStateFlags state;
4756 for (StringView stateString : statesString.split(',')) {
4757 if (equalLettersIgnoringASCIICase(stateString, "audio"_s))
4758 state.add(MediaProducerMutedState::AudioIsMuted);
4759 if (equalLettersIgnoringASCIICase(stateString, "capturedevices"_s))
4760 state.add(MediaProducer::AudioAndVideoCaptureIsMuted);
4761 if (equalLettersIgnoringASCIICase(stateString, "screencapture"_s))
4762 state.add(MediaProducerMutedState::ScreenCaptureIsMuted);
4763 }
4764
4765 if (Page* page = document->page())
4766 page->setMuted(state);
4767}
4768
4769String Internals::pageMediaState()
4770{
4771 Document* document = contextDocument();
4772 if (!document || !document->page())
4773 return emptyString();
4774
4775 auto state = document->page()->mediaState();
4776 StringBuilder string;
4777 if (state.containsAny(MediaProducerMediaState::IsPlayingAudio))
4778 string.append("IsPlayingAudio,");
4779 if (state.containsAny(MediaProducerMediaState::IsPlayingVideo))
4780 string.append("IsPlayingVideo,");
4781 if (state.containsAny(MediaProducerMediaState::IsPlayingToExternalDevice))
4782 string.append("IsPlayingToExternalDevice,");
4783 if (state.containsAny(MediaProducerMediaState::RequiresPlaybackTargetMonitoring))
4784 string.append("RequiresPlaybackTargetMonitoring,");
4785 if (state.containsAny(MediaProducerMediaState::ExternalDeviceAutoPlayCandidate))
4786 string.append("ExternalDeviceAutoPlayCandidate,");
4787 if (state.containsAny(MediaProducerMediaState::DidPlayToEnd))
4788 string.append("DidPlayToEnd,");
4789 if (state.containsAny(MediaProducerMediaState::IsSourceElementPlaying))
4790 string.append("IsSourceElementPlaying,");
4791
4792 if (state.containsAny(MediaProducerMediaState::IsNextTrackControlEnabled))
4793 string.append("IsNextTrackControlEnabled,");
4794 if (state.containsAny(MediaProducerMediaState::IsPreviousTrackControlEnabled))
4795 string.append("IsPreviousTrackControlEnabled,");
4796
4797 if (state.containsAny(MediaProducerMediaState::HasPlaybackTargetAvailabilityListener))
4798 string.append("HasPlaybackTargetAvailabilityListener,");
4799 if (state.containsAny(MediaProducerMediaState::HasAudioOrVideo))
4800 string.append("HasAudioOrVideo,");
4801
4802 if (state.containsAny(MediaProducerMediaState::HasActiveAudioCaptureDevice))
4803 string.append("HasActiveAudioCaptureDevice,");
4804 if (state.containsAny(MediaProducerMediaState::HasMutedAudioCaptureDevice))
4805 string.append("HasMutedAudioCaptureDevice,");
4806 if (state.containsAny(MediaProducerMediaState::HasInterruptedAudioCaptureDevice))
4807 string.append("HasInterruptedAudioCaptureDevice,");
4808
4809 if (state.containsAny(MediaProducerMediaState::HasActiveVideoCaptureDevice))
4810 string.append("HasActiveVideoCaptureDevice,");
4811 if (state.containsAny(MediaProducerMediaState::HasMutedVideoCaptureDevice))
4812 string.append("HasMutedVideoCaptureDevice,");
4813 if (state.containsAny(MediaProducerMediaState::HasInterruptedVideoCaptureDevice))
4814 string.append("HasInterruptedVideoCaptureDevice,");
4815
4816 if (state.containsAny(MediaProducerMediaState::HasUserInteractedWithMediaElement))
4817 string.append("HasUserInteractedWithMediaElement,");
4818
4819 if (state.containsAny(MediaProducerMediaState::HasActiveScreenCaptureDevice))
4820 string.append("HasActiveScreenCaptureDevice,");
4821 if (state.containsAny(MediaProducerMediaState::HasMutedScreenCaptureDevice))
4822 string.append("HasMutedScreenCaptureDevice,");
4823
4824 if (state.containsAny(MediaProducerMediaState::HasActiveWindowCaptureDevice))
4825 string.append("HasActiveWindowCaptureDevice,");
4826 if (state.containsAny(MediaProducerMediaState::HasMutedWindowCaptureDevice))
4827 string.append("HasMutedWindowCaptureDevice,");
4828 if (state.containsAny(MediaProducerMediaState::HasInterruptedWindowCaptureDevice))
4829 string.append("HasInterruptedWindowCaptureDevice,");
4830
4831 if (state.containsAny(MediaProducerMediaState::HasActiveSystemAudioCaptureDevice))
4832 string.append("HasActiveSystemAudioCaptureDevice,");
4833 if (state.containsAny(MediaProducerMediaState::HasMutedSystemAudioCaptureDevice))
4834 string.append("HasMutedSystemAudioCaptureDevice,");
4835 if (state.containsAny(MediaProducerMediaState::HasInterruptedSystemAudioCaptureDevice))
4836 string.append("HasInterruptedSystemAudioCaptureDevice,");
4837
4838 if (string.isEmpty())
4839 string.append("IsNotPlaying");
4840 else
4841 string.shrink(string.length() - 1);
4842
4843 return string.toString();
4844}
4845
4846void Internals::setPageDefersLoading(bool defersLoading)
4847{
4848 Document* document = contextDocument();
4849 if (!document)
4850 return;
4851 if (Page* page = document->page())
4852 page->setDefersLoading(defersLoading);
4853}
4854
4855ExceptionOr<bool> Internals::pageDefersLoading()
4856{
4857 Document* document = contextDocument();
4858 if (!document || !document->page())
4859 return Exception { InvalidAccessError };
4860 return document->page()->defersLoading();
4861}
4862
4863RefPtr<File> Internals::createFile(const String& path)
4864{
4865 Document* document = contextDocument();
4866 if (!document)
4867 return nullptr;
4868
4869 URL url = document->completeURL(path);
4870 if (!url.isLocalFile())
4871 return nullptr;
4872
4873 return File::create(document, url.fileSystemPath());
4874}
4875
4876String Internals::createTemporaryFile(const String& name, const String& contents)
4877{
4878 if (name.isEmpty())
4879 return nullString();
4880
4881 auto file = FileSystem::invalidPlatformFileHandle;
4882 auto path = FileSystem::openTemporaryFile(makeString("WebCoreTesting-", name), file);
4883 if (!FileSystem::isHandleValid(file))
4884 return nullString();
4885
4886 auto contentsUTF8 = contents.utf8();
4887 FileSystem::writeToFile(file, contentsUTF8.data(), contentsUTF8.length());
4888
4889 FileSystem::closeFile(file);
4890
4891 return path;
4892}
4893
4894void Internals::queueMicroTask(int testNumber)
4895{
4896 Document* document = contextDocument();
4897 if (!document)
4898 return;
4899
4900 ScriptExecutionContext* context = document;
4901 auto& eventLoop = context->eventLoop();
4902 eventLoop.queueMicrotask([document = Ref { *document }, testNumber]() {
4903 document->addConsoleMessage(MessageSource::JS, MessageLevel::Debug, makeString("MicroTask #", testNumber, " has run."));
4904 });
4905}
4906
4907#if ENABLE(CONTENT_FILTERING)
4908
4909MockContentFilterSettings& Internals::mockContentFilterSettings()
4910{
4911 return MockContentFilterSettings::singleton();
4912}
4913
4914#endif
4915
4916static void appendOffsets(StringBuilder& builder, const Vector<SnapOffset<LayoutUnit>>& snapOffsets)
4917{
4918 bool justStarting = true;
4919
4920 builder.append("{ ");
4921 for (auto& coordinate : snapOffsets) {
4922 if (!justStarting)
4923 builder.append(", ");
4924 else
4925 justStarting = false;
4926 builder.append(coordinate.offset.toUnsigned());
4927 if (coordinate.stop == ScrollSnapStop::Always)
4928 builder.append(" (always)");
4929
4930 }
4931 builder.append(" }");
4932}
4933
4934void Internals::setPlatformMomentumScrollingPredictionEnabled(bool enabled)
4935{
4936 ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled(enabled);
4937}
4938
4939ExceptionOr<String> Internals::scrollSnapOffsets(Element& element)
4940{
4941 auto areaOrException = scrollableAreaForNode(&element);
4942 if (areaOrException.hasException())
4943 return areaOrException.releaseException();
4944
4945 auto* scrollableArea = areaOrException.releaseReturnValue();
4946 if (!scrollableArea)
4947 return Exception { InvalidAccessError };
4948
4949 auto* offsetInfo = scrollableArea->snapOffsetsInfo();
4950 StringBuilder result;
4951 if (offsetInfo && !offsetInfo->horizontalSnapOffsets.isEmpty()) {
4952 result.append("horizontal = ");
4953 appendOffsets(result, offsetInfo->horizontalSnapOffsets);
4954 }
4955
4956 if (offsetInfo && !offsetInfo->verticalSnapOffsets.isEmpty()) {
4957 if (result.length())
4958 result.append(", ");
4959 result.append("vertical = ");
4960 appendOffsets(result, offsetInfo->verticalSnapOffsets);
4961 }
4962
4963 return result.toString();
4964}
4965
4966ExceptionOr<bool> Internals::isScrollSnapInProgress(Element& element)
4967{
4968 auto areaOrException = scrollableAreaForNode(&element);
4969 if (areaOrException.hasException())
4970 return areaOrException.releaseException();
4971
4972 auto* scrollableArea = areaOrException.releaseReturnValue();
4973 if (!scrollableArea)
4974 return Exception { InvalidAccessError };
4975
4976 return scrollableArea->isScrollSnapInProgress();
4977}
4978
4979bool Internals::testPreloaderSettingViewport()
4980{
4981 return testPreloadScannerViewportSupport(contextDocument());
4982}
4983
4984ExceptionOr<String> Internals::pathStringWithShrinkWrappedRects(const Vector<double>& rectComponents, double radius)
4985{
4986 if (rectComponents.size() % 4)
4987 return Exception { InvalidAccessError };
4988
4989 Vector<FloatRect> rects;
4990 for (unsigned i = 0; i < rectComponents.size(); i += 4)
4991 rects.append(FloatRect(rectComponents[i], rectComponents[i + 1], rectComponents[i + 2], rectComponents[i + 3]));
4992
4993 SVGPathStringBuilder builder;
4994 PathUtilities::pathWithShrinkWrappedRects(rects, radius).apply([&builder](const PathElement& element) {
4995 switch (element.type) {
4996 case PathElement::Type::MoveToPoint:
4997 builder.moveTo(element.points[0], false, AbsoluteCoordinates);
4998 return;
4999 case PathElement::Type::AddLineToPoint:
5000 builder.lineTo(element.points[0], AbsoluteCoordinates);
5001 return;
5002 case PathElement::Type::AddQuadCurveToPoint:
5003 builder.curveToQuadratic(element.points[0], element.points[1], AbsoluteCoordinates);
5004 return;
5005 case PathElement::Type::AddCurveToPoint:
5006 builder.curveToCubic(element.points[0], element.points[1], element.points[2], AbsoluteCoordinates);
5007 return;
5008 case PathElement::Type::CloseSubpath:
5009 builder.closePath();
5010 return;
5011 }
5012 ASSERT_NOT_REACHED();
5013 });
5014 return builder.result();
5015}
5016
5017void Internals::systemBeep()
5018{
5019 SystemSoundManager::singleton().systemBeep();
5020}
5021
5022#if ENABLE(VIDEO)
5023
5024String Internals::getCurrentMediaControlsStatusForElement(HTMLMediaElement& mediaElement)
5025{
5026 return mediaElement.getCurrentMediaControlsStatus();
5027}
5028
5029void Internals::setMediaControlsMaximumRightContainerButtonCountOverride(HTMLMediaElement& mediaElement, size_t count)
5030{
5031 mediaElement.setMediaControlsMaximumRightContainerButtonCountOverride(count);
5032}
5033
5034void Internals::setMediaControlsHidePlaybackRates(HTMLMediaElement& mediaElement, bool hidePlaybackRates)
5035{
5036 mediaElement.setMediaControlsHidePlaybackRates(hidePlaybackRates);
5037}
5038
5039#endif // ENABLE(VIDEO)
5040
5041void Internals::setPageMediaVolume(float volume)
5042{
5043 Document* document = contextDocument();
5044 if (!document)
5045 return;
5046
5047 Page* page = document->page();
5048 if (!page)
5049 return;
5050
5051 page->setMediaVolume(volume);
5052}
5053
5054#if !PLATFORM(COCOA)
5055
5056String Internals::userVisibleString(const DOMURL& url)
5057{
5058 return WTF::URLHelpers::userVisibleURL(url.href().string().utf8());
5059}
5060
5061#endif
5062
5063void Internals::setShowAllPlugins(bool show)
5064{
5065 Document* document = contextDocument();
5066 if (!document)
5067 return;
5068
5069 Page* page = document->page();
5070 if (!page)
5071 return;
5072
5073 page->setShowAllPlugins(show);
5074}
5075
5076bool Internals::isReadableStreamDisturbed(JSC::JSGlobalObject& lexicalGlobalObject, JSValue stream)
5077{
5078 return ReadableStream::isDisturbed(lexicalGlobalObject, stream);
5079}
5080
5081JSValue Internals::cloneArrayBuffer(JSC::JSGlobalObject& lexicalGlobalObject, JSValue buffer, JSValue srcByteOffset, JSValue srcLength)
5082{
5083 auto& vm = lexicalGlobalObject.vm();
5084 const Identifier& privateName = builtinNames(vm).cloneArrayBufferPrivateName();
5085 JSValue value;
5086 PropertySlot propertySlot(value, PropertySlot::InternalMethodType::Get);
5087 lexicalGlobalObject.methodTable()->getOwnPropertySlot(&lexicalGlobalObject, &lexicalGlobalObject, privateName, propertySlot);
5088 value = propertySlot.getValue(&lexicalGlobalObject, privateName);
5089 ASSERT(value.isCallable());
5090
5091 JSObject* function = value.getObject();
5092 auto callData = JSC::getCallData(function);
5093 ASSERT(callData.type != JSC::CallData::Type::None);
5094 MarkedArgumentBuffer arguments;
5095 arguments.append(buffer);
5096 arguments.append(srcByteOffset);
5097 arguments.append(srcLength);
5098 ASSERT(!arguments.hasOverflowed());
5099
5100 return JSC::call(&lexicalGlobalObject, function, callData, JSC::jsUndefined(), arguments);
5101}
5102
5103String Internals::resourceLoadStatisticsForURL(const DOMURL& url)
5104{
5105 return ResourceLoadObserver::shared().statisticsForURL(url.href());
5106}
5107
5108void Internals::setResourceLoadStatisticsEnabled(bool enable)
5109{
5110 DeprecatedGlobalSettings::setResourceLoadStatisticsEnabled(enable);
5111}
5112
5113String Internals::composedTreeAsText(Node& node)
5114{
5115 if (!is<ContainerNode>(node))
5116 return emptyString();
5117 return WebCore::composedTreeAsText(downcast<ContainerNode>(node));
5118}
5119
5120bool Internals::isProcessingUserGesture()
5121{
5122 return UserGestureIndicator::processingUserGesture();
5123}
5124
5125void Internals::withUserGesture(RefPtr<VoidCallback>&& callback)
5126{
5127 UserGestureIndicator gestureIndicator(ProcessingUserGesture, contextDocument());
5128 callback->handleEvent();
5129}
5130
5131void Internals::withoutUserGesture(RefPtr<VoidCallback>&& callback)
5132{
5133 UserGestureIndicator gestureIndicator(NotProcessingUserGesture, contextDocument());
5134 callback->handleEvent();
5135}
5136
5137bool Internals::userIsInteracting()
5138{
5139 if (auto* document = contextDocument()) {
5140 if (auto* page = document->page())
5141 return page->chrome().client().userIsInteracting();
5142 }
5143 return false;
5144}
5145
5146bool Internals::hasTransientActivation()
5147{
5148 if (auto* document = contextDocument()) {
5149 if (auto* window = document->domWindow())
5150 return window->hasTransientActivation();
5151 }
5152 return false;
5153}
5154
5155double Internals::lastHandledUserGestureTimestamp()
5156{
5157 Document* document = contextDocument();
5158 if (!document)
5159 return 0;
5160
5161 return document->lastHandledUserGestureTimestamp().secondsSinceEpoch().value();
5162}
5163
5164RefPtr<GCObservation> Internals::observeGC(JSC::JSValue value)
5165{
5166 if (!value.isObject())
5167 return nullptr;
5168 return GCObservation::create(asObject(value));
5169}
5170
5171void Internals::setUserInterfaceLayoutDirection(UserInterfaceLayoutDirection userInterfaceLayoutDirection)
5172{
5173 Document* document = contextDocument();
5174 if (!document)
5175 return;
5176
5177 Page* page = document->page();
5178 if (!page)
5179 return;
5180
5181 page->setUserInterfaceLayoutDirection(userInterfaceLayoutDirection == UserInterfaceLayoutDirection::LTR ? WebCore::UserInterfaceLayoutDirection::LTR : WebCore::UserInterfaceLayoutDirection::RTL);
5182}
5183
5184#if !PLATFORM(COCOA)
5185
5186bool Internals::userPrefersReducedMotion() const
5187{
5188 return false;
5189}
5190
5191bool Internals::userPrefersContrast() const
5192{
5193 return false;
5194}
5195
5196#if ENABLE(VIDEO)
5197double Internals::privatePlayerVolume(const HTMLMediaElement&)
5198{
5199 return 0;
5200}
5201
5202bool Internals::privatePlayerMuted(const HTMLMediaElement&)
5203{
5204 return false;
5205}
5206#endif
5207
5208RefPtr<SharedBuffer> Internals::pngDataForTesting()
5209{
5210 return nullptr;
5211}
5212
5213#endif // !PLATFORM(COCOA)
5214
5215#if ENABLE(VIDEO)
5216bool Internals::isMediaElementHidden(const HTMLMediaElement& media)
5217{
5218 return media.elementIsHidden();
5219}
5220
5221double Internals::elementEffectivePlaybackRate(const HTMLMediaElement& media)
5222{
5223 return media.effectivePlaybackRate();
5224}
5225#endif
5226
5227ExceptionOr<void> Internals::setIsPlayingToBluetoothOverride(std::optional<bool> isPlaying)
5228{
5229#if ENABLE(ROUTING_ARBITRATION)
5230 AudioSession::sharedSession().setIsPlayingToBluetoothOverride(isPlaying);
5231 return { };
5232#else
5233 UNUSED_PARAM(isPlaying);
5234 return Exception { NotSupportedError };
5235#endif
5236}
5237
5238void Internals::reportBacktrace()
5239{
5240 WTFReportBacktrace();
5241}
5242
5243void Internals::setBaseWritingDirection(BaseWritingDirection direction)
5244{
5245 if (auto* document = contextDocument()) {
5246 if (auto* frame = document->frame()) {
5247 switch (direction) {
5248 case BaseWritingDirection::Ltr:
5249 frame->editor().setBaseWritingDirection(WritingDirection::LeftToRight);
5250 break;
5251 case BaseWritingDirection::Rtl:
5252 frame->editor().setBaseWritingDirection(WritingDirection::RightToLeft);
5253 break;
5254 case BaseWritingDirection::Natural:
5255 frame->editor().setBaseWritingDirection(WritingDirection::Natural);
5256 break;
5257 }
5258 }
5259 }
5260}
5261
5262#if ENABLE(POINTER_LOCK)
5263bool Internals::pageHasPendingPointerLock() const
5264{
5265 Document* document = contextDocument();
5266 if (!document)
5267 return false;
5268
5269 Page* page = document->page();
5270 if (!page)
5271 return false;
5272
5273 return page->pointerLockController().lockPending();
5274}
5275
5276bool Internals::pageHasPointerLock() const
5277{
5278 Document* document = contextDocument();
5279 if (!document)
5280 return false;
5281
5282 Page* page = document->page();
5283 if (!page)
5284 return false;
5285
5286 auto& controller = page->pointerLockController();
5287 return controller.element() && !controller.lockPending();
5288}
5289#endif
5290
5291void Internals::markContextAsInsecure()
5292{
5293 auto* document = contextDocument();
5294 if (!document)
5295 return;
5296
5297 document->securityOrigin().setIsPotentiallyTrustworthy(false);
5298}
5299
5300void Internals::postTask(RefPtr<VoidCallback>&& callback)
5301{
5302 auto* document = contextDocument();
5303 if (!document) {
5304 callback->handleEvent();
5305 return;
5306 }
5307
5308 document->postTask([callback = WTFMove(callback)](ScriptExecutionContext&) {
5309 callback->handleEvent();
5310 });
5311}
5312
5313static std::optional<TaskSource> taskSourceFromString(const String& taskSourceName)
5314{
5315 if (taskSourceName == "DOMManipulation"_s)
5316 return TaskSource::DOMManipulation;
5317 return std::nullopt;
5318}
5319
5320ExceptionOr<void> Internals::queueTask(ScriptExecutionContext& context, const String& taskSourceName, RefPtr<VoidCallback>&& callback)
5321{
5322 auto source = taskSourceFromString(taskSourceName);
5323 if (!source)
5324 return Exception { NotSupportedError };
5325
5326 context.eventLoop().queueTask(*source, [callback = WTFMove(callback)] {
5327 callback->handleEvent();
5328 });
5329
5330 return { };
5331}
5332
5333ExceptionOr<void> Internals::queueTaskToQueueMicrotask(Document& document, const String& taskSourceName, RefPtr<VoidCallback>&& callback)
5334{
5335 auto source = taskSourceFromString(taskSourceName);
5336 if (!source)
5337 return Exception { NotSupportedError };
5338
5339 ScriptExecutionContext& context = document; // This avoids unnecessarily exporting Document::eventLoop.
5340 context.eventLoop().queueTask(*source, [movedCallback = WTFMove(callback), protectedDocument = Ref { document }]() mutable {
5341 ScriptExecutionContext& context = protectedDocument.get();
5342 context.eventLoop().queueMicrotask([callback = WTFMove(movedCallback)] {
5343 callback->handleEvent();
5344 });
5345 });
5346
5347 return { };
5348}
5349
5350ExceptionOr<bool> Internals::hasSameEventLoopAs(WindowProxy& proxy)
5351{
5352 RefPtr<ScriptExecutionContext> context = contextDocument();
5353 if (!context || !proxy.frame())
5354 return Exception { InvalidStateError };
5355
5356 auto& proxyFrame = *proxy.frame();
5357 if (!is<Frame>(proxyFrame))
5358 return false;
5359 RefPtr<ScriptExecutionContext> proxyContext = downcast<Frame>(proxyFrame).document();
5360 if (!proxyContext)
5361 return Exception { InvalidStateError };
5362
5363 return context->eventLoop().hasSameEventLoopAs(proxyContext->eventLoop());
5364}
5365
5366Vector<String> Internals::accessKeyModifiers() const
5367{
5368 Vector<String> accessKeyModifierStrings;
5369
5370 for (auto modifier : EventHandler::accessKeyModifiers()) {
5371 switch (modifier) {
5372 case PlatformEvent::Modifier::AltKey:
5373 accessKeyModifierStrings.append("altKey"_s);
5374 break;
5375 case PlatformEvent::Modifier::ControlKey:
5376 accessKeyModifierStrings.append("ctrlKey"_s);
5377 break;
5378 case PlatformEvent::Modifier::MetaKey:
5379 accessKeyModifierStrings.append("metaKey"_s);
5380 break;
5381 case PlatformEvent::Modifier::ShiftKey:
5382 accessKeyModifierStrings.append("shiftKey"_s);
5383 break;
5384 case PlatformEvent::Modifier::CapsLockKey:
5385 accessKeyModifierStrings.append("capsLockKey"_s);
5386 break;
5387 case PlatformEvent::Modifier::AltGraphKey:
5388 ASSERT_NOT_REACHED(); // AltGraph is only for DOM API.
5389 break;
5390 }
5391 }
5392
5393 return accessKeyModifierStrings;
5394}
5395
5396void Internals::setQuickLookPassword(const String& password)
5397{
5398#if PLATFORM(IOS_FAMILY) && USE(QUICK_LOOK)
5399 auto& quickLookHandleClient = MockPreviewLoaderClient::singleton();
5400 LegacyPreviewLoader::setClientForTesting(&quickLookHandleClient);
5401 quickLookHandleClient.setPassword(password);
5402#else
5403 UNUSED_PARAM(password);
5404#endif
5405}
5406
5407void Internals::setAsRunningUserScripts(Document& document)
5408{
5409 document.setAsRunningUserScripts();
5410}
5411
5412#if ENABLE(WEBGL)
5413void Internals::simulateEventForWebGLContext(SimulatedWebGLContextEvent event, WebGLRenderingContext& context)
5414{
5415 WebGLRenderingContext::SimulatedEventForTesting contextEvent;
5416 switch (event) {
5417 case SimulatedWebGLContextEvent::ContextChange:
5418 contextEvent = WebGLRenderingContext::SimulatedEventForTesting::ContextChange;
5419 break;
5420 case SimulatedWebGLContextEvent::GPUStatusFailure:
5421 contextEvent = WebGLRenderingContext::SimulatedEventForTesting::GPUStatusFailure;
5422 break;
5423 case SimulatedWebGLContextEvent::Timeout:
5424 contextEvent = WebGLRenderingContext::SimulatedEventForTesting::Timeout;
5425 break;
5426 default:
5427 ASSERT_NOT_REACHED();
5428 return;
5429 }
5430 context.simulateEventForTesting(contextEvent);
5431}
5432
5433bool Internals::hasLowAndHighPowerGPUs()
5434{
5435#if PLATFORM(MAC)
5436 return WebCore::hasLowAndHighPowerGPUs();
5437#else
5438 return false;
5439#endif
5440}
5441
5442Internals::RequestedGPU Internals::requestedGPU(WebGLRenderingContext& context)
5443{
5444 UNUSED_PARAM(context);
5445 if (auto optionalAttributes = context.getContextAttributes()) {
5446 auto attributes = *optionalAttributes;
5447 if (attributes.forceRequestForHighPerformanceGPU)
5448 return RequestedGPU::HighPerformance;
5449 switch (attributes.powerPreference) {
5450 case GraphicsContextGLPowerPreference::Default:
5451 return RequestedGPU::Default;
5452 case GraphicsContextGLPowerPreference::LowPower:
5453 return RequestedGPU::LowPower;
5454 case GraphicsContextGLPowerPreference::HighPerformance:
5455 return RequestedGPU::HighPerformance;
5456 }
5457 }
5458
5459 return RequestedGPU::Default;
5460}
5461
5462bool Internals::requestedMetal(WebGLRenderingContext& context)
5463{
5464 UNUSED_PARAM(context);
5465#if PLATFORM(COCOA)
5466 if (auto optionalAttributes = context.getContextAttributes()) {
5467 auto attributes = *optionalAttributes;
5468
5469 return attributes.useMetal;
5470 }
5471#endif
5472
5473 return false;
5474}
5475#endif
5476
5477void Internals::setPageVisibility(bool isVisible)
5478{
5479 updatePageActivityState(ActivityState::IsVisible, isVisible);
5480}
5481
5482void Internals::setPageIsFocused(bool isFocused)
5483{
5484 updatePageActivityState(ActivityState::IsFocused, isFocused);
5485}
5486
5487void Internals::setPageIsFocusedAndActive(bool isFocusedAndActive)
5488{
5489 updatePageActivityState({ ActivityState::IsFocused, ActivityState::WindowIsActive }, isFocusedAndActive);
5490}
5491
5492void Internals::setPageIsInWindow(bool isInWindow)
5493{
5494 updatePageActivityState(ActivityState::IsInWindow, isInWindow);
5495}
5496
5497void Internals::updatePageActivityState(OptionSet<ActivityState::Flag> statesToChange, bool newValue)
5498{
5499 auto* page = contextDocument() ? contextDocument()->page() : nullptr;
5500 if (!page)
5501 return;
5502 auto state = page->activityState();
5503
5504 if (!newValue)
5505 state.remove(statesToChange);
5506 else
5507 state.add(statesToChange);
5508
5509 page->setActivityState(state);
5510}
5511
5512bool Internals::isPageActive() const
5513{
5514 auto* document = contextDocument();
5515 if (!document || !document->page())
5516 return false;
5517 auto& page = *document->page();
5518 return page.activityState().contains(ActivityState::WindowIsActive);
5519}
5520
5521#if ENABLE(WEB_RTC)
5522void Internals::setH264HardwareEncoderAllowed(bool allowed)
5523{
5524 LibWebRTCProvider::setH264HardwareEncoderAllowed(allowed);
5525}
5526#endif
5527
5528#if ENABLE(MEDIA_STREAM)
5529void Internals::setMockAudioTrackChannelNumber(MediaStreamTrack& track, unsigned short channelNumber)
5530{
5531 auto& source = track.source();
5532 if (!is<MockRealtimeAudioSource>(source))
5533 return;
5534 downcast<MockRealtimeAudioSource>(source).setChannelCount(channelNumber);
5535}
5536
5537void Internals::setCameraMediaStreamTrackOrientation(MediaStreamTrack& track, int orientation)
5538{
5539 auto& source = track.source();
5540 if (!source.isCaptureSource())
5541 return;
5542 m_orientationNotifier.orientationChanged(orientation);
5543 source.monitorOrientation(m_orientationNotifier);
5544}
5545
5546void Internals::stopObservingRealtimeMediaSource()
5547{
5548 if (!m_trackSource)
5549 return;
5550
5551 switch (m_trackSource->type()) {
5552 case RealtimeMediaSource::Type::Audio:
5553 m_trackSource->removeAudioSampleObserver(*this);
5554 break;
5555 case RealtimeMediaSource::Type::Video:
5556 m_trackSource->removeVideoFrameObserver(*this);
5557 break;
5558 }
5559 m_trackSource->removeObserver(*this);
5560
5561 m_trackSource = nullptr;
5562 m_trackAudioSampleCount = 0;
5563 m_trackVideoSampleCount = 0;
5564}
5565
5566void Internals::observeMediaStreamTrack(MediaStreamTrack& track)
5567{
5568 stopObservingRealtimeMediaSource();
5569
5570 m_trackVideoRotation = -1;
5571 m_trackSource = &track.source();
5572 m_trackSource->addObserver(*this);
5573 switch (m_trackSource->type()) {
5574 case RealtimeMediaSource::Type::Audio:
5575 m_trackSource->addAudioSampleObserver(*this);
5576 break;
5577 case RealtimeMediaSource::Type::Video:
5578 m_trackSource->addVideoFrameObserver(*this);
5579 break;
5580 }
5581}
5582
5583void Internals::grabNextMediaStreamTrackFrame(TrackFramePromise&& promise)
5584{
5585 m_nextTrackFramePromise = makeUnique<TrackFramePromise>(WTFMove(promise));
5586}
5587
5588void Internals::mediaStreamTrackVideoFrameRotation(DOMPromiseDeferred<IDLShort>&& promise)
5589{
5590 promise.resolve(m_trackVideoRotation);
5591}
5592
5593void Internals::videoFrameAvailable(VideoFrame& videoFrame, VideoFrameTimeMetadata)
5594{
5595 callOnMainThread([this, weakThis = WeakPtr { *this }, videoFrame = Ref { videoFrame }] {
5596 if (!weakThis)
5597 return;
5598 m_trackVideoSampleCount++;
5599 m_trackVideoRotation = static_cast<int>(videoFrame->rotation());
5600 if (!m_nextTrackFramePromise)
5601 return;
5602
5603 auto& videoSettings = m_trackSource->settings();
5604 if (!videoSettings.width() || !videoSettings.height())
5605 return;
5606
5607 auto rgba = videoFrame->getRGBAImageData();
5608 if (!rgba)
5609 return;
5610
5611 auto imageData = ImageData::create(rgba.releaseNonNull(), videoSettings.width(), videoSettings.height(), { { PredefinedColorSpace::SRGB } });
5612 if (!imageData.hasException())
5613 m_nextTrackFramePromise->resolve(imageData.releaseReturnValue());
5614 else
5615 m_nextTrackFramePromise->reject(imageData.exception().code());
5616 m_nextTrackFramePromise = nullptr;
5617 });
5618}
5619
5620void Internals::delayMediaStreamTrackSamples(MediaStreamTrack& track, float delay)
5621{
5622 track.source().delaySamples(Seconds { delay });
5623}
5624
5625void Internals::setMediaStreamTrackMuted(MediaStreamTrack& track, bool muted)
5626{
5627 track.source().setMuted(muted);
5628}
5629
5630void Internals::removeMediaStreamTrack(MediaStream& stream, MediaStreamTrack& track)
5631{
5632 stream.privateStream().removeTrack(track.privateTrack());
5633}
5634
5635void Internals::simulateMediaStreamTrackCaptureSourceFailure(MediaStreamTrack& track)
5636{
5637 track.source().captureFailed();
5638}
5639
5640void Internals::setMediaStreamTrackIdentifier(MediaStreamTrack& track, String&& id)
5641{
5642 track.setIdForTesting(WTFMove(id));
5643}
5644
5645void Internals::setMediaStreamSourceInterrupted(MediaStreamTrack& track, bool interrupted)
5646{
5647 track.source().setInterruptedForTesting(interrupted);
5648}
5649
5650bool Internals::isMediaStreamSourceInterrupted(MediaStreamTrack& track) const
5651{
5652 return track.source().interrupted();
5653}
5654
5655bool Internals::isMediaStreamSourceEnded(MediaStreamTrack& track) const
5656{
5657 return track.source().isEnded();
5658}
5659
5660bool Internals::isMockRealtimeMediaSourceCenterEnabled()
5661{
5662 return MockRealtimeMediaSourceCenter::mockRealtimeMediaSourceCenterEnabled();
5663}
5664
5665bool Internals::shouldAudioTrackPlay(const AudioTrack& track)
5666{
5667 if (!is<AudioTrackPrivateMediaStream>(track.privateTrack()))
5668 return false;
5669 return downcast<AudioTrackPrivateMediaStream>(track.privateTrack()).shouldPlay();
5670}
5671#endif
5672
5673bool Internals::supportsAudioSession() const
5674{
5675#if USE(AUDIO_SESSION)
5676 return true;
5677#else
5678 return false;
5679#endif
5680}
5681
5682auto Internals::audioSessionCategory() const -> AudioSessionCategory
5683{
5684#if USE(AUDIO_SESSION)
5685 return AudioSession::sharedSession().category();
5686#else
5687 return AudioSessionCategory::None;
5688#endif
5689}
5690
5691auto Internals::routeSharingPolicy() const -> RouteSharingPolicy
5692{
5693#if USE(AUDIO_SESSION)
5694 return AudioSession::sharedSession().routeSharingPolicy();
5695#else
5696 return RouteSharingPolicy::Default;
5697#endif
5698}
5699
5700#if ENABLE(VIDEO)
5701auto Internals::categoryAtMostRecentPlayback(HTMLMediaElement& element) const -> AudioSessionCategory
5702{
5703#if USE(AUDIO_SESSION)
5704 return element.categoryAtMostRecentPlayback();
5705#else
5706 UNUSED_PARAM(element);
5707 return AudioSessionCategory::None;
5708#endif
5709}
5710#endif
5711
5712double Internals::preferredAudioBufferSize() const
5713{
5714#if USE(AUDIO_SESSION)
5715 return AudioSession::sharedSession().preferredBufferSize();
5716#endif
5717 return 0;
5718}
5719
5720double Internals::currentAudioBufferSize() const
5721{
5722#if USE(AUDIO_SESSION)
5723 return AudioSession::sharedSession().bufferSize();
5724#endif
5725 return 0;
5726}
5727
5728
5729bool Internals::audioSessionActive() const
5730{
5731#if USE(AUDIO_SESSION)
5732 return AudioSession::sharedSession().isActive();
5733#endif
5734 return false;
5735}
5736
5737void Internals::storeRegistrationsOnDisk(DOMPromiseDeferred<void>&& promise)
5738{
5739#if ENABLE(SERVICE_WORKER)
5740 if (!contextDocument())
5741 return;
5742
5743 auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnection();
5744 connection.storeRegistrationsOnDiskForTesting([promise = WTFMove(promise)]() mutable {
5745 promise.resolve();
5746 });
5747#else
5748 promise.resolve();
5749#endif
5750}
5751
5752void Internals::sendH2Ping(String url, DOMPromiseDeferred<IDLDouble>&& promise)
5753{
5754 auto* document = contextDocument();
5755 if (!document) {
5756 promise.reject(InvalidStateError);
5757 return;
5758 }
5759
5760 auto* frame = document->frame();
5761 if (!frame) {
5762 promise.reject(InvalidStateError);
5763 return;
5764 }
5765
5766 frame->loader().client().sendH2Ping(URL { url }, [promise = WTFMove(promise)] (Expected<Seconds, ResourceError>&& result) mutable {
5767 if (result.has_value())
5768 promise.resolve(result.value().value());
5769 else
5770 promise.reject(InvalidStateError);
5771 });
5772}
5773
5774void Internals::clearCacheStorageMemoryRepresentation(DOMPromiseDeferred<void>&& promise)
5775{
5776 auto* document = contextDocument();
5777 if (!document)
5778 return;
5779
5780 if (!m_cacheStorageConnection) {
5781 if (auto* page = contextDocument()->page())
5782 m_cacheStorageConnection = page->cacheStorageProvider().createCacheStorageConnection();
5783 if (!m_cacheStorageConnection)
5784 return;
5785 }
5786 m_cacheStorageConnection->clearMemoryRepresentation(ClientOrigin { document->topOrigin().data(), document->securityOrigin().data() }, [promise = WTFMove(promise)] (auto && result) mutable {
5787 ASSERT_UNUSED(result, !result);
5788 promise.resolve();
5789 });
5790}
5791
5792void Internals::cacheStorageEngineRepresentation(DOMPromiseDeferred<IDLDOMString>&& promise)
5793{
5794 auto* document = contextDocument();
5795 if (!document)
5796 return;
5797
5798 if (!m_cacheStorageConnection) {
5799 if (auto* page = contextDocument()->page())
5800 m_cacheStorageConnection = page->cacheStorageProvider().createCacheStorageConnection();
5801 if (!m_cacheStorageConnection)
5802 return;
5803 }
5804 m_cacheStorageConnection->engineRepresentation([promise = WTFMove(promise)](const String& result) mutable {
5805 promise.resolve(result);
5806 });
5807}
5808
5809void Internals::updateQuotaBasedOnSpaceUsage()
5810{
5811 auto* document = contextDocument();
5812 if (!document)
5813 return;
5814
5815 if (!m_cacheStorageConnection) {
5816 if (auto* page = contextDocument()->page())
5817 m_cacheStorageConnection = page->cacheStorageProvider().createCacheStorageConnection();
5818 if (!m_cacheStorageConnection)
5819 return;
5820 }
5821
5822 m_cacheStorageConnection->updateQuotaBasedOnSpaceUsage(ClientOrigin { document->topOrigin().data(), document->securityOrigin().data() });
5823}
5824
5825void Internals::setConsoleMessageListener(RefPtr<StringCallback>&& listener)
5826{
5827 if (!contextDocument())
5828 return;
5829
5830 contextDocument()->setConsoleMessageListener(WTFMove(listener));
5831}
5832
5833void Internals::setResponseSizeWithPadding(FetchResponse& response, uint64_t size)
5834{
5835 response.setBodySizeWithPadding(size);
5836}
5837
5838uint64_t Internals::responseSizeWithPadding(FetchResponse& response) const
5839{
5840 return response.bodySizeWithPadding();
5841}
5842
5843const String& Internals::responseNetworkLoadMetricsProtocol(const FetchResponse& response)
5844{
5845 return response.networkLoadMetrics().protocol;
5846}
5847
5848#if ENABLE(SERVICE_WORKER)
5849void Internals::hasServiceWorkerRegistration(const String& clientURL, HasRegistrationPromise&& promise)
5850{
5851 if (!contextDocument())
5852 return;
5853
5854 URL parsedURL = contextDocument()->completeURL(clientURL);
5855
5856 return ServiceWorkerProvider::singleton().serviceWorkerConnection().matchRegistration(SecurityOriginData { contextDocument()->topOrigin().data() }, parsedURL, [promise = WTFMove(promise)] (auto&& result) mutable {
5857 promise.resolve(!!result);
5858 });
5859}
5860
5861void Internals::terminateServiceWorker(ServiceWorker& worker, DOMPromiseDeferred<void>&& promise)
5862{
5863 ServiceWorkerProvider::singleton().terminateWorkerForTesting(worker.identifier(), [promise = WTFMove(promise)]() mutable {
5864 promise.resolve();
5865 });
5866}
5867
5868void Internals::whenServiceWorkerIsTerminated(ServiceWorker& worker, DOMPromiseDeferred<void>&& promise)
5869{
5870 return ServiceWorkerProvider::singleton().serviceWorkerConnection().whenServiceWorkerIsTerminatedForTesting(worker.identifier(), [promise = WTFMove(promise)]() mutable {
5871 promise.resolve();
5872 });
5873}
5874#endif
5875
5876#if ENABLE(APPLE_PAY)
5877MockPaymentCoordinator& Internals::mockPaymentCoordinator(Document& document)
5878{
5879 return downcast<MockPaymentCoordinator>(document.frame()->page()->paymentCoordinator().client());
5880}
5881#endif
5882
5883Internals::ImageOverlayLine::~ImageOverlayLine() = default;
5884Internals::ImageOverlayText::~ImageOverlayText() = default;
5885Internals::ImageOverlayBlock::~ImageOverlayBlock() = default;
5886Internals::ImageOverlayDataDetector::~ImageOverlayDataDetector() = default;
5887
5888#if ENABLE(IMAGE_ANALYSIS)
5889
5890template<typename T>
5891static FloatQuad getQuad(const T& overlayTextOrLine)
5892{
5893 return {
5894 FloatPoint(overlayTextOrLine.topLeft->x(), overlayTextOrLine.topLeft->y()),
5895 FloatPoint(overlayTextOrLine.topRight->x(), overlayTextOrLine.topRight->y()),
5896 FloatPoint(overlayTextOrLine.bottomRight->x(), overlayTextOrLine.bottomRight->y()),
5897 FloatPoint(overlayTextOrLine.bottomLeft->x(), overlayTextOrLine.bottomLeft->y()),
5898 };
5899}
5900
5901static TextRecognitionLineData makeDataForLine(const Internals::ImageOverlayLine& line)
5902{
5903 return {
5904 getQuad<Internals::ImageOverlayLine>(line),
5905 line.children.map([](auto& textChild) -> TextRecognitionWordData {
5906 return { textChild.text, getQuad(textChild), textChild.hasLeadingWhitespace };
5907 }),
5908 line.hasTrailingNewline
5909 };
5910}
5911
5912void Internals::requestTextRecognition(Element& element, RefPtr<VoidCallback>&& callback)
5913{
5914 auto page = contextDocument()->page();
5915 if (!page) {
5916 if (callback)
5917 callback->handleEvent();
5918 }
5919
5920 page->chrome().client().requestTextRecognition(element, { }, [callback = WTFMove(callback)] (auto&&) {
5921 if (callback)
5922 callback->handleEvent();
5923 });
5924}
5925
5926RefPtr<Element> Internals::textRecognitionCandidate() const
5927{
5928 if (RefPtr frame = contextDocument()->frame())
5929 return frame->eventHandler().textRecognitionCandidateElement();
5930
5931 return nullptr;
5932}
5933
5934#endif // ENABLE(IMAGE_ANALYSIS)
5935
5936void Internals::installImageOverlay(Element& element, Vector<ImageOverlayLine>&& lines, Vector<ImageOverlayBlock>&& blocks, Vector<ImageOverlayDataDetector>&& dataDetectors)
5937{
5938 if (!is<HTMLElement>(element))
5939 return;
5940
5941#if ENABLE(IMAGE_ANALYSIS)
5942 ImageOverlay::updateWithTextRecognitionResult(downcast<HTMLElement>(element), TextRecognitionResult {
5943 lines.map([] (auto& line) -> TextRecognitionLineData {
5944 return makeDataForLine(line);
5945 })
5946#if ENABLE(DATA_DETECTION)
5947 , dataDetectors.map([](auto& dataDetector) -> TextRecognitionDataDetector {
5948 return { fakeDataDetectorResultForTesting(), { getQuad(dataDetector) } };
5949 })
5950#endif // ENABLE(DATA_DETECTION)
5951 , blocks.map([] (auto& block) {
5952 return TextRecognitionBlockData { block.text, getQuad(block) };
5953 })
5954#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
5955 , fakeImageAnalysisResultForTesting(lines)
5956#endif
5957 });
5958#else
5959 UNUSED_PARAM(blocks);
5960 UNUSED_PARAM(dataDetectors);
5961 UNUSED_PARAM(lines);
5962#endif
5963}
5964
5965bool Internals::hasActiveDataDetectorHighlight() const
5966{
5967#if ENABLE(DATA_DETECTION) && ENABLE(IMAGE_ANALYSIS)
5968 if (auto* controller = contextDocument()->page()->imageOverlayControllerIfExists())
5969 return controller->hasActiveDataDetectorHighlightForTesting();
5970#endif
5971 return false;
5972}
5973
5974bool Internals::isSystemPreviewLink(Element& element) const
5975{
5976#if USE(SYSTEM_PREVIEW)
5977 return is<HTMLAnchorElement>(element) && downcast<HTMLAnchorElement>(element).isSystemPreviewLink();
5978#else
5979 UNUSED_PARAM(element);
5980 return false;
5981#endif
5982}
5983
5984bool Internals::isSystemPreviewImage(Element& element) const
5985{
5986#if USE(SYSTEM_PREVIEW)
5987 if (is<HTMLImageElement>(element))
5988 return downcast<HTMLImageElement>(element).isSystemPreviewImage();
5989 if (is<HTMLPictureElement>(element))
5990 return downcast<HTMLPictureElement>(element).isSystemPreviewImage();
5991 return false;
5992#else
5993 UNUSED_PARAM(element);
5994 return false;
5995#endif
5996}
5997
5998bool Internals::usingAppleInternalSDK() const
5999{
6000#if USE(APPLE_INTERNAL_SDK)
6001 return true;
6002#else
6003 return false;
6004#endif
6005}
6006
6007bool Internals::usingGStreamer() const
6008{
6009#if USE(GSTREAMER)
6010 return true;
6011#else
6012 return false;
6013#endif
6014}
6015
6016void Internals::setCaptureExtraNetworkLoadMetricsEnabled(bool value)
6017{
6018 platformStrategies()->loaderStrategy()->setCaptureExtraNetworkLoadMetricsEnabled(value);
6019}
6020
6021String Internals::ongoingLoadsDescriptions() const
6022{
6023 StringBuilder builder;
6024 builder.append('[');
6025 bool isStarting = true;
6026 for (auto& identifier : platformStrategies()->loaderStrategy()->ongoingLoads()) {
6027 if (isStarting)
6028 isStarting = false;
6029 else
6030 builder.append(',');
6031
6032 builder.append('[');
6033
6034 for (auto& info : platformStrategies()->loaderStrategy()->intermediateLoadInformationFromResourceLoadIdentifier(identifier))
6035 builder.append('[', (int)info.type, ",\"", info.request.url().string(), "\",\"", info.request.httpMethod(), "\",", info.response.httpStatusCode(), ']');
6036
6037 builder.append(']');
6038 }
6039 builder.append(']');
6040 return builder.toString();
6041}
6042
6043void Internals::reloadWithoutContentExtensions()
6044{
6045 if (auto* frame = this->frame())
6046 frame->loader().reload(ReloadOption::DisableContentBlockers);
6047}
6048
6049void Internals::setUseSystemAppearance(bool value)
6050{
6051 if (!contextDocument() || !contextDocument()->page())
6052 return;
6053 contextDocument()->page()->setUseSystemAppearance(value);
6054}
6055
6056size_t Internals::pluginCount()
6057{
6058 if (!contextDocument() || !contextDocument()->page())
6059 return 0;
6060
6061 return contextDocument()->page()->pluginData().webVisiblePlugins().size();
6062}
6063
6064void Internals::notifyResourceLoadObserver()
6065{
6066 ResourceLoadObserver::shared().updateCentralStatisticsStore([] { });
6067}
6068
6069unsigned Internals::primaryScreenDisplayID()
6070{
6071#if PLATFORM(COCOA)
6072 return WebCore::primaryScreenDisplayID();
6073#else
6074 return 0;
6075#endif
6076}
6077
6078bool Internals::capsLockIsOn()
6079{
6080 return WebCore::PlatformKeyboardEvent::currentCapsLockState();
6081}
6082
6083auto Internals::parseHEVCCodecParameters(StringView string) -> std::optional<HEVCParameterSet>
6084{
6085 return WebCore::parseHEVCCodecParameters(string);
6086}
6087
6088String Internals::createHEVCCodecParametersString(const HEVCParameterSet& parameters)
6089{
6090 return WebCore::createHEVCCodecParametersString(parameters);
6091}
6092
6093auto Internals::parseDoViCodecParameters(StringView string) -> std::optional<DoViParameterSet>
6094{
6095 auto parseResult = WebCore::parseDoViCodecParameters(string);
6096 if (!parseResult)
6097 return std::nullopt;
6098 DoViParameterSet convertedResult;
6099 switch (parseResult->codec) {
6100 case DoViParameters::Codec::AVC1:
6101 convertedResult.codecName = "avc1"_s;
6102 break;
6103 case DoViParameters::Codec::AVC3:
6104 convertedResult.codecName = "avc3"_s;
6105 break;
6106 case DoViParameters::Codec::HEV1:
6107 convertedResult.codecName = "hev1"_s;
6108 break;
6109 case DoViParameters::Codec::HVC1:
6110 convertedResult.codecName = "hvc1"_s;
6111 break;
6112 }
6113 convertedResult.bitstreamProfileID = parseResult->bitstreamProfileID;
6114 convertedResult.bitstreamLevelID = parseResult->bitstreamLevelID;
6115 return convertedResult;
6116}
6117
6118String Internals::createDoViCodecParametersString(const DoViParameterSet& parameterSet)
6119{
6120 DoViParameters::Codec codec;
6121 if (parameterSet.codecName == "avc1"_s)
6122 codec = DoViParameters::Codec::AVC1;
6123 else if (parameterSet.codecName == "avc3"_s)
6124 codec = DoViParameters::Codec::AVC3;
6125 else if (parameterSet.codecName == "hev1"_s)
6126 codec = DoViParameters::Codec::HEV1;
6127 else if (parameterSet.codecName == "hvc1"_s)
6128 codec = DoViParameters::Codec::HVC1;
6129 else
6130 return emptyString();
6131
6132 return WebCore::createDoViCodecParametersString({ codec, parameterSet.bitstreamProfileID, parameterSet.bitstreamLevelID });
6133}
6134
6135std::optional<VPCodecConfigurationRecord> Internals::parseVPCodecParameters(StringView string)
6136{
6137 return WebCore::parseVPCodecParameters(string);
6138}
6139
6140auto Internals::getCookies() const -> Vector<CookieData>
6141{
6142 auto* document = contextDocument();
6143 if (!document)
6144 return { };
6145
6146 auto* page = document->page();
6147 if (!page)
6148 return { };
6149
6150 Vector<Cookie> cookies;
6151 page->cookieJar().getRawCookies(*document, document->cookieURL(), cookies);
6152 return WTF::map(cookies, [](auto& cookie) {
6153 return CookieData { cookie };
6154 });
6155}
6156
6157void Internals::setAlwaysAllowLocalWebarchive(bool alwaysAllowLocalWebarchive)
6158{
6159 auto* localFrame = frame();
6160 if (!localFrame)
6161 return;
6162 localFrame->loader().setAlwaysAllowLocalWebarchive(alwaysAllowLocalWebarchive);
6163}
6164
6165void Internals::processWillSuspend()
6166{
6167#if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
6168 PlatformMediaSessionManager::sharedManager().processWillSuspend();
6169#endif
6170}
6171
6172void Internals::processDidResume()
6173{
6174#if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
6175 PlatformMediaSessionManager::sharedManager().processDidResume();
6176#endif
6177}
6178
6179void Internals::testDictionaryLogging()
6180{
6181 auto* document = contextDocument();
6182 if (!document)
6183 return;
6184
6185 auto* page = document->page();
6186 if (!page)
6187 return;
6188
6189 DiagnosticLoggingClient::ValueDictionary dictionary;
6190 dictionary.set("stringKey"_s, String("stringValue"_s));
6191 dictionary.set("uint64Key"_s, std::numeric_limits<uint64_t>::max());
6192 dictionary.set("int64Key"_s, std::numeric_limits<int64_t>::min());
6193 dictionary.set("boolKey"_s, true);
6194 dictionary.set("doubleKey"_s, 2.7182818284590452353602874);
6195
6196 page->diagnosticLoggingClient().logDiagnosticMessageWithValueDictionary("testMessage"_s, "testDescription"_s, dictionary, ShouldSample::No);
6197}
6198
6199void Internals::setMaximumIntervalForUserGestureForwardingForFetch(double interval)
6200{
6201 UserGestureToken::setMaximumIntervalForUserGestureForwardingForFetchForTesting(Seconds(interval));
6202}
6203
6204void Internals::setTransientActivationDuration(double seconds)
6205{
6206 DOMWindow::overrideTransientActivationDurationForTesting(Seconds { seconds });
6207}
6208
6209void Internals::setIsPlayingToAutomotiveHeadUnit(bool isPlaying)
6210{
6211#if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
6212 PlatformMediaSessionManager::sharedManager().setIsPlayingToAutomotiveHeadUnit(isPlaying);
6213#endif
6214}
6215
6216String Internals::highlightPseudoElementColor(const AtomString& highlightName, Element& element)
6217{
6218 element.document().updateStyleIfNeeded();
6219
6220 auto& styleResolver = element.document().styleScope().resolver();
6221 auto* parentStyle = element.computedStyle();
6222 if (!parentStyle)
6223 return { };
6224
6225 auto style = styleResolver.pseudoStyleForElement(element, { PseudoId::Highlight, highlightName }, { parentStyle });
6226 if (!style)
6227 return { };
6228
6229 return serializationForCSS(style->color());
6230}
6231
6232Internals::TextIndicatorInfo::TextIndicatorInfo()
6233{
6234}
6235
6236Internals::TextIndicatorInfo::TextIndicatorInfo(const WebCore::TextIndicatorData& data)
6237 : textBoundingRectInRootViewCoordinates(DOMRect::create(data.textBoundingRectInRootViewCoordinates))
6238 , textRectsInBoundingRectCoordinates(DOMRectList::create(data.textRectsInBoundingRectCoordinates))
6239{
6240}
6241
6242Internals::TextIndicatorInfo::~TextIndicatorInfo() = default;
6243
6244Internals::TextIndicatorInfo Internals::textIndicatorForRange(const Range& range, TextIndicatorOptions options)
6245{
6246 auto indicator = TextIndicator::createWithRange(makeSimpleRange(range), options.coreOptions(), TextIndicatorPresentationTransition::None);
6247 return indicator->data();
6248}
6249
6250void Internals::addPrefetchLoadEventListener(HTMLLinkElement& link, RefPtr<EventListener>&& listener)
6251{
6252 if (link.document().settings().linkPrefetchEnabled() && equalLettersIgnoringASCIICase(link.rel(), "prefetch"_s)) {
6253 link.allowPrefetchLoadAndErrorForTesting();
6254 link.addEventListener(eventNames().loadEvent, listener.releaseNonNull(), false);
6255 }
6256}
6257
6258#if ENABLE(WEB_AUTHN)
6259void Internals::setMockWebAuthenticationConfiguration(const MockWebAuthenticationConfiguration& configuration)
6260{
6261 auto* document = contextDocument();
6262 if (!document)
6263 return;
6264 auto* page = document->page();
6265 if (!page)
6266 return;
6267 page->chrome().client().setMockWebAuthenticationConfiguration(configuration);
6268}
6269#endif
6270
6271void Internals::setMaxCanvasPixelMemory(unsigned size)
6272{
6273 HTMLCanvasElement::setMaxPixelMemoryForTesting(size);
6274}
6275
6276void Internals::setMaxCanvasArea(unsigned size)
6277{
6278 HTMLCanvasElement::setMaxCanvasAreaForTesting(size);
6279}
6280
6281int Internals::processIdentifier() const
6282{
6283 return getCurrentProcessID();
6284}
6285
6286Ref<InternalsMapLike> Internals::createInternalsMapLike()
6287{
6288 return InternalsMapLike::create();
6289}
6290
6291Ref<InternalsSetLike> Internals::createInternalsSetLike()
6292{
6293 return InternalsSetLike::create();
6294}
6295
6296bool Internals::hasSandboxMachLookupAccessToGlobalName(const String& process, const String& service)
6297{
6298#if PLATFORM(COCOA)
6299 pid_t pid;
6300 if (process == "com.apple.WebKit.WebContent"_s)
6301 pid = getpid();
6302 else
6303 RELEASE_ASSERT_NOT_REACHED();
6304
6305 return !sandbox_check(pid, "mach-lookup", static_cast<enum sandbox_filter_type>(SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT), service.utf8().data());
6306#else
6307 UNUSED_PARAM(process);
6308 UNUSED_PARAM(service);
6309 return false;
6310#endif
6311}
6312
6313bool Internals::hasSandboxMachLookupAccessToXPCServiceName(const String& process, const String& service)
6314{
6315#if PLATFORM(COCOA)
6316 pid_t pid;
6317 if (process == "com.apple.WebKit.WebContent"_s)
6318 pid = getpid();
6319 else
6320 RELEASE_ASSERT_NOT_REACHED();
6321
6322 return !sandbox_check(pid, "mach-lookup", static_cast<enum sandbox_filter_type>(SANDBOX_FILTER_XPC_SERVICE_NAME | SANDBOX_CHECK_NO_REPORT), service.utf8().data());
6323#else
6324 UNUSED_PARAM(process);
6325 UNUSED_PARAM(service);
6326 return false;
6327#endif
6328}
6329
6330String Internals::windowLocationHost(DOMWindow& window)
6331{
6332 return window.location().host();
6333}
6334
6335String Internals::systemColorForCSSValue(const String& cssValue, bool useDarkModeAppearance, bool useElevatedUserInterfaceLevel)
6336{
6337 CSSValueID id = cssValueKeywordID(cssValue);
6338 RELEASE_ASSERT(StyleColor::isSystemColorKeyword(id));
6339
6340 OptionSet<StyleColorOptions> options;
6341 if (useDarkModeAppearance)
6342 options.add(StyleColorOptions::UseDarkAppearance);
6343 if (useElevatedUserInterfaceLevel)
6344 options.add(StyleColorOptions::UseElevatedUserInterfaceLevel);
6345
6346 return serializationForCSS(RenderTheme::singleton().systemColor(id, options));
6347}
6348
6349bool Internals::systemHasBattery() const
6350{
6351#if PLATFORM(COCOA)
6352 return WebCore::systemHasBattery();
6353#else
6354 return false;
6355#endif
6356}
6357
6358void Internals::setSystemHasBatteryForTesting(bool hasBattery)
6359{
6360#if PLATFORM(COCOA)
6361 SystemBatteryStatusTestingOverrides::singleton().setHasBattery(hasBattery);
6362#else
6363 UNUSED_PARAM(hasBattery);
6364#endif
6365}
6366
6367void Internals::setSystemHasACForTesting(bool hasAC)
6368{
6369#if PLATFORM(COCOA)
6370 SystemBatteryStatusTestingOverrides::singleton().setHasAC(hasAC);
6371#else
6372 UNUSED_PARAM(hasAC);
6373#endif
6374}
6375
6376void Internals::setHardwareVP9DecoderDisabledForTesting(bool disabled)
6377{
6378#if ENABLE(VP9) && PLATFORM(COCOA)
6379 VP9TestingOverrides::singleton().setHardwareDecoderDisabled(disabled);
6380#else
6381 UNUSED_PARAM(disabled);
6382#endif
6383}
6384
6385void Internals::setVP9ScreenSizeAndScaleForTesting(double width, double height, double scale)
6386{
6387#if ENABLE(VP9) && PLATFORM(COCOA)
6388 VP9TestingOverrides::singleton().setVP9ScreenSizeAndScale(ScreenDataOverrides { width, height, scale });
6389#else
6390 UNUSED_PARAM(width);
6391 UNUSED_PARAM(height);
6392 UNUSED_PARAM(scale);
6393#endif
6394}
6395
6396int Internals::readPreferenceInteger(const String& domain, const String& key)
6397{
6398#if PLATFORM(COCOA)
6399 Boolean keyExistsAndHasValidFormat = false;
6400 return CFPreferencesGetAppIntegerValue(key.createCFString().get(), domain.createCFString().get(), &keyExistsAndHasValidFormat);
6401#else
6402 UNUSED_PARAM(domain);
6403 UNUSED_PARAM(key);
6404 return -1;
6405#endif
6406}
6407
6408#if !PLATFORM(COCOA)
6409String Internals::encodedPreferenceValue(const String&, const String&)
6410{
6411 return emptyString();
6412}
6413
6414String Internals::getUTIFromTag(const String&, const String&, const String&)
6415{
6416 return emptyString();
6417}
6418
6419bool Internals::isRemoteUIAppForAccessibility()
6420{
6421 return false;
6422}
6423
6424bool Internals::hasSandboxIOKitOpenAccessToClass(const String& process, const String& ioKitClass)
6425{
6426 UNUSED_PARAM(process);
6427 UNUSED_PARAM(ioKitClass);
6428 return false;
6429}
6430#endif
6431
6432#if ENABLE(APP_HIGHLIGHTS)
6433Vector<String> Internals::appHighlightContextMenuItemTitles() const
6434{
6435 return {{
6436 contextMenuItemTagAddHighlightToCurrentQuickNote(),
6437 contextMenuItemTagAddHighlightToNewQuickNote(),
6438 }};
6439}
6440
6441unsigned Internals::numberOfAppHighlights()
6442{
6443 Document* document = contextDocument();
6444 if (!document)
6445 return 0;
6446 auto appHighlightRegister = document->appHighlightRegisterIfExists();
6447 if (!appHighlightRegister)
6448 return 0;
6449 unsigned numHighlights = 0;
6450 for (auto& highlight : appHighlightRegister->map())
6451 numHighlights += highlight.value->rangesData().size();
6452 return numHighlights;
6453}
6454#endif
6455
6456bool Internals::supportsPictureInPicture()
6457{
6458 return WebCore::supportsPictureInPicture();
6459}
6460
6461String Internals::focusRingColor()
6462{
6463 return serializationForCSS(RenderTheme::singleton().focusRingColor({ }));
6464}
6465
6466unsigned Internals::createSleepDisabler(const String& reason, bool display)
6467{
6468 static unsigned lastUsedIdentifier = 0;
6469 auto sleepDisabler = makeUnique<WebCore::SleepDisabler>(reason, display ? PAL::SleepDisabler::Type::Display : PAL::SleepDisabler::Type::System);
6470 m_sleepDisablers.add(++lastUsedIdentifier, WTFMove(sleepDisabler));
6471 return lastUsedIdentifier;
6472}
6473
6474bool Internals::destroySleepDisabler(unsigned identifier)
6475{
6476 return m_sleepDisablers.remove(identifier);
6477}
6478
6479#if ENABLE(WEBXR)
6480
6481ExceptionOr<RefPtr<WebXRTest>> Internals::xrTest()
6482{
6483 auto* document = contextDocument();
6484 if (!document || !document->domWindow() || !document->settings().webXREnabled())
6485 return Exception { InvalidAccessError };
6486
6487 if (!m_xrTest) {
6488 auto* navigator = contextDocument()->domWindow()->optionalNavigator();
6489 if (!navigator)
6490 return Exception { InvalidAccessError };
6491
6492 m_xrTest = WebXRTest::create(NavigatorWebXR::xr(*navigator));
6493 }
6494 return m_xrTest.get();
6495}
6496
6497#endif
6498
6499#if ENABLE(ENCRYPTED_MEDIA)
6500unsigned Internals::mediaKeysInternalInstanceObjectRefCount(const MediaKeys& mediaKeys) const
6501{
6502 return mediaKeys.internalInstanceObjectRefCount();
6503}
6504
6505unsigned Internals::mediaKeySessionInternalInstanceSessionObjectRefCount(const MediaKeySession& mediaKeySession) const
6506{
6507 return mediaKeySession.internalInstanceSessionObjectRefCount();
6508}
6509#endif
6510
6511void Internals::setContentSizeCategory(Internals::ContentSizeCategory category)
6512{
6513#if PLATFORM(IOS)
6514 CFStringRef ctCategory = nil;
6515 switch (category) {
6516 case Internals::ContentSizeCategory::L:
6517 ctCategory = kCTFontContentSizeCategoryL;
6518 break;
6519 case Internals::ContentSizeCategory::XXXL:
6520 ctCategory = kCTFontContentSizeCategoryXXXL;
6521 break;
6522 }
6523 WebCore::setContentSizeCategory(ctCategory);
6524 Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
6525#else
6526 UNUSED_PARAM(category);
6527#endif
6528}
6529
6530#if ENABLE(ATTACHMENT_ELEMENT)
6531
6532ExceptionOr<Internals::AttachmentThumbnailInfo> Internals::attachmentThumbnailInfo(const HTMLAttachmentElement& element)
6533{
6534#if HAVE(QUICKLOOK_THUMBNAILING)
6535 AttachmentThumbnailInfo info;
6536 if (auto image = element.thumbnail()) {
6537 auto size = image->size();
6538 info.width = size.width();
6539 info.height = size.height();
6540 }
6541 return info;
6542#else
6543 UNUSED_PARAM(element);
6544 return Exception { InvalidAccessError };
6545#endif
6546}
6547
6548#if ENABLE(SERVICE_CONTROLS)
6549bool Internals::hasImageControls(const HTMLImageElement& element) const
6550{
6551 return ImageControlsMac::hasImageControls(element);
6552}
6553#endif
6554
6555#endif // ENABLE(ATTACHMENT_ELEMENT)
6556
6557#if ENABLE(MEDIA_SESSION)
6558ExceptionOr<double> Internals::currentMediaSessionPosition(const MediaSession& session)
6559{
6560 if (auto currentPosition = session.currentPosition())
6561 return *currentPosition;
6562 return Exception { InvalidStateError };
6563}
6564
6565ExceptionOr<void> Internals::sendMediaSessionAction(MediaSession& session, const MediaSessionActionDetails& actionDetails)
6566{
6567 if (session.callActionHandler(actionDetails))
6568 return { };
6569
6570 return Exception { InvalidStateError };
6571}
6572
6573void Internals::loadArtworkImage(String&& url, ArtworkImagePromise&& promise)
6574{
6575 if (!contextDocument()) {
6576 promise.reject(Exception { InvalidStateError, "No document."_s });
6577 return;
6578 }
6579 if (m_artworkImagePromise) {
6580 promise.reject(Exception { InvalidStateError, "Another download is currently pending."_s });
6581 return;
6582 }
6583 m_artworkImagePromise = makeUnique<ArtworkImagePromise>(WTFMove(promise));
6584 m_artworkLoader = makeUnique<ArtworkImageLoader>(*contextDocument(), url, [this](Image* image) {
6585 if (image) {
6586 auto imageData = ImageData::create(image->width(), image->height(), { { PredefinedColorSpace::SRGB } });
6587 if (!imageData.hasException())
6588 m_artworkImagePromise->resolve(imageData.releaseReturnValue());
6589 else
6590 m_artworkImagePromise->reject(imageData.exception().code());
6591 } else
6592 m_artworkImagePromise->reject(Exception { InvalidAccessError, "No image retrieved."_s });
6593 m_artworkImagePromise = nullptr;
6594 });
6595 m_artworkLoader->requestImageResource();
6596}
6597
6598ExceptionOr<Vector<String>> Internals::platformSupportedCommands() const
6599{
6600 if (!contextDocument())
6601 return Exception { InvalidAccessError };
6602 auto commands = PlatformMediaSessionManager::sharedManager().supportedCommands();
6603 Vector<String> commandStrings;
6604 for (auto command : commands)
6605 commandStrings.append(convertEnumerationToString(command));
6606
6607 return commandStrings;
6608}
6609
6610#endif
6611
6612#if ENABLE(MEDIA_SESSION_COORDINATOR)
6613ExceptionOr<void> Internals::registerMockMediaSessionCoordinator(ScriptExecutionContext& context, RefPtr<StringCallback>&& listener)
6614{
6615 if (m_mockMediaSessionCoordinator)
6616 return { };
6617
6618 auto* document = contextDocument();
6619 if (!document || !document->domWindow())
6620 return Exception { InvalidAccessError };
6621
6622 if (!document->settings().mediaSessionCoordinatorEnabled())
6623 return Exception { InvalidAccessError };
6624
6625 auto& session = NavigatorMediaSession::mediaSession(document->domWindow()->navigator());
6626 auto mock = MockMediaSessionCoordinator::create(context, WTFMove(listener));
6627 m_mockMediaSessionCoordinator = mock.ptr();
6628 session.coordinator().setMediaSessionCoordinatorPrivate(WTFMove(mock));
6629
6630 return { };
6631}
6632
6633ExceptionOr<void> Internals::setMockMediaSessionCoordinatorCommandsShouldFail(bool shouldFail)
6634{
6635 if (!m_mockMediaSessionCoordinator)
6636 return Exception { InvalidStateError };
6637
6638 m_mockMediaSessionCoordinator->setCommandsShouldFail(shouldFail);
6639 return { };
6640}
6641#endif // ENABLE(MEDIA_SESSION)
6642
6643constexpr ASCIILiteral string(PartialOrdering ordering)
6644{
6645 if (is_lt(ordering))
6646 return "less"_s;
6647 if (is_gt(ordering))
6648 return "greater"_s;
6649 if (is_eq(ordering))
6650 return "equivalent"_s;
6651 return "unordered"_s;
6652}
6653
6654constexpr TreeType convertType(Internals::TreeType type)
6655{
6656 switch (type) {
6657 case Internals::Tree:
6658 return Tree;
6659 case Internals::ShadowIncludingTree:
6660 return ShadowIncludingTree;
6661 case Internals::ComposedTree:
6662 return ComposedTree;
6663 }
6664 ASSERT_NOT_REACHED();
6665 return Tree;
6666}
6667
6668String Internals::treeOrder(Node& a, Node& b, TreeType type)
6669{
6670 return string(treeOrderForTesting(convertType(type), a, b));
6671}
6672
6673String Internals::treeOrderBoundaryPoints(Node& containerA, unsigned offsetA, Node& containerB, unsigned offsetB, TreeType type)
6674{
6675 return string(treeOrderForTesting(convertType(type), { containerA, offsetA }, { containerB, offsetB }));
6676}
6677
6678bool Internals::rangeContainsNode(const AbstractRange& range, Node& node, TreeType type)
6679{
6680 return containsForTesting(convertType(type), makeSimpleRange(range), node);
6681}
6682
6683bool Internals::rangeContainsBoundaryPoint(const AbstractRange& range, Node& container, unsigned offset, TreeType type)
6684{
6685 return containsForTesting(convertType(type), makeSimpleRange(range), { container, offset });
6686}
6687
6688bool Internals::rangeContainsRange(const AbstractRange& outerRange, const AbstractRange& innerRange, TreeType type)
6689{
6690 return containsForTesting(convertType(type), makeSimpleRange(outerRange), makeSimpleRange(innerRange));
6691}
6692
6693bool Internals::rangeIntersectsNode(const AbstractRange& range, Node& node, TreeType type)
6694{
6695 return intersectsForTesting(convertType(type), makeSimpleRange(range), node);
6696}
6697
6698bool Internals::rangeIntersectsRange(const AbstractRange& a, const AbstractRange& b, TreeType type)
6699{
6700 return intersectsForTesting(convertType(type), makeSimpleRange(a), makeSimpleRange(b));
6701}
6702
6703String Internals::dumpStyleResolvers()
6704{
6705 auto* document = contextDocument();
6706 if (!document || !document->domWindow())
6707 return { };
6708
6709 document->updateStyleIfNeeded();
6710
6711 unsigned currentIdentifier = 0;
6712 HashMap<Style::Resolver*, unsigned> resolverIdentifiers;
6713
6714 StringBuilder result;
6715
6716 auto dumpResolver = [&](auto name, auto& resolver) {
6717 auto identifier = resolverIdentifiers.ensure(&resolver, [&] {
6718 return currentIdentifier++;
6719 }).iterator->value;
6720
6721 result.append("(", name, " ");
6722 result.append("(identifier=", identifier, ") ");
6723 result.append("(author rule count=", resolver.ruleSets().authorStyle().ruleCount(), ")");
6724 result.append(")\n");
6725 };
6726
6727 dumpResolver("document resolver", document->styleScope().resolver());
6728
6729 for (auto* shadowRoot : document->inDocumentShadowRoots()) {
6730 auto* name = shadowRoot->mode() == ShadowRootMode::UserAgent ? "shadow root resolver (user agent)" : "shadow root resolver (author)";
6731 dumpResolver(name, shadowRoot->styleScope().resolver());
6732 }
6733
6734 return result.toString();
6735}
6736
6737ExceptionOr<void> Internals::setDocumentAutoplayPolicy(Document& document, Internals::AutoplayPolicy policy)
6738{
6739 static_assert(static_cast<uint8_t>(WebCore::AutoplayPolicy::Default) == static_cast<uint8_t>(Internals::AutoplayPolicy::Default), "Internals::Default != WebCore::Default");
6740 static_assert(static_cast<uint8_t>(WebCore::AutoplayPolicy::Allow) == static_cast<uint8_t>(Internals::AutoplayPolicy::Allow), "Internals::Allow != WebCore::Allow");
6741 static_assert(static_cast<uint8_t>(WebCore::AutoplayPolicy::AllowWithoutSound) == static_cast<uint8_t>(Internals::AutoplayPolicy::AllowWithoutSound), "Internals::AllowWithoutSound != WebCore::AllowWithoutSound");
6742 static_assert(static_cast<uint8_t>(WebCore::AutoplayPolicy::Deny) == static_cast<uint8_t>(Internals::AutoplayPolicy::Deny), "Internals::Deny != WebCore::Deny");
6743
6744 auto* loader = document.loader();
6745 if (!loader)
6746 return Exception { InvalidStateError };
6747
6748 loader->setAutoplayPolicy(static_cast<WebCore::AutoplayPolicy>(policy));
6749
6750 return { };
6751}
6752
6753#if ENABLE(WEBGL) && !PLATFORM(COCOA)
6754bool Internals::platformSupportsMetal(bool)
6755{
6756 return false;
6757}
6758#endif
6759
6760void Internals::retainTextIteratorForDocumentContent()
6761{
6762 auto* document = contextDocument();
6763 if (!document)
6764 return;
6765
6766 auto range = makeRangeSelectingNodeContents(*document);
6767 m_textIterator = makeUnique<TextIterator>(range);
6768}
6769
6770#if ENABLE(SERVICE_WORKER)
6771RefPtr<PushSubscription> Internals::createPushSubscription(const String& endpoint, std::optional<EpochTimeStamp> expirationTime, const ArrayBuffer& serverVAPIDPublicKey, const ArrayBuffer& clientECDHPublicKey, const ArrayBuffer& auth)
6772{
6773 auto myEndpoint = endpoint;
6774 Vector<uint8_t> myServerVAPIDPublicKey { static_cast<const uint8_t*>(serverVAPIDPublicKey.data()), serverVAPIDPublicKey.byteLength() };
6775 Vector<uint8_t> myClientECDHPublicKey { static_cast<const uint8_t*>(clientECDHPublicKey.data()), clientECDHPublicKey.byteLength() };
6776 Vector<uint8_t> myAuth { static_cast<const uint8_t*>(auth.data()), auth.byteLength() };
6777
6778 return PushSubscription::create(PushSubscriptionData { { }, WTFMove(myEndpoint), expirationTime, WTFMove(myServerVAPIDPublicKey), WTFMove(myClientECDHPublicKey), WTFMove(myAuth) });
6779}
6780#endif
6781
6782void Internals::overrideModalContainerSearchTermForTesting(AtomString&& term)
6783{
6784 if (auto observer = contextDocument()->modalContainerObserver())
6785 observer->overrideSearchTermForTesting(WTFMove(term));
6786}
6787
6788#if ENABLE(ARKIT_INLINE_PREVIEW_MAC)
6789
6790void Internals::modelInlinePreviewUUIDs(ModelInlinePreviewUUIDsPromise&& promise) const
6791{
6792 auto* document = contextDocument();
6793 if (!document) {
6794 promise.reject(InvalidStateError);
6795 return;
6796 }
6797
6798 auto* frame = document->frame();
6799 if (!frame) {
6800 promise.reject(InvalidStateError);
6801 return;
6802 }
6803
6804 CompletionHandler<void(Vector<String>&&)> completionHandler = [promise = WTFMove(promise)] (Vector<String> uuids) mutable {
6805 promise.resolve(uuids);
6806 };
6807
6808 frame->loader().client().modelInlinePreviewUUIDs(WTFMove(completionHandler));
6809}
6810
6811String Internals::modelInlinePreviewUUIDForModelElement(const HTMLModelElement& modelElement) const
6812{
6813 return modelElement.inlinePreviewUUIDForTesting();
6814}
6815
6816#endif
6817
6818} // namespace WebCore
Note: See TracBrowser for help on using the repository browser.