Bug Summary

File:Source/WebKit/WebProcess/FullScreen/WebFullScreenManager.cpp
Warning:line 95, column 21
Call argument for 'this' parameter is unchecked and unsafe

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple arm64-apple-ios26.2.0-simulator -Os -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name UnifiedSource116.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=osx -analyzer-checker=security.insecureAPI.decodeValueOfObjCType -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=non-leaf -ffp-contract=on -fno-rounding-math -target-sdk-version=26.2 -fdepfile-entry=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/SDKSettings.json -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -fno-modulemap-allow-subdirectory-search -fdefine-target-os-macros -enable-tlsdesc -target-cpu apple-m1 -target-feature +v8.4a -target-feature +aes -target-feature +altnzcv -target-feature +ccdp -target-feature +ccpp -target-feature +complxnum -target-feature +crc -target-feature +dotprod -target-feature +flagm -target-feature +fp-armv8 -target-feature +fp16fml -target-feature +fptoint -target-feature +fullfp16 -target-feature +jsconv -target-feature +lse -target-feature +neon -target-feature +pauth -target-feature +perfmon -target-feature +predres -target-feature +ras -target-feature +rcpc -target-feature +rdm -target-feature +sb -target-feature +sha2 -target-feature +sha3 -target-feature +specrestrict -target-feature +ssbs -target-abi darwinpcs -dwarf-ext-refs -fmodule-format=obj -debug-info-kind=standalone -dwarf-version=5 -debugger-tuning=lldb -fdebug-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit -target-linker-version 1230.1 -fcoverage-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit -resource-dir /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21 -dependency-file /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebKit/WebKit/normal/arm64/UnifiedSource116.d -skip-unused-modulemap-deps -MT dependencies -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-generated-files.hmap -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-project-headers.hmap -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/usr/local/include -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/usr/include -iframework /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/System/Library/Frameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/PrivateFrameworks -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/usr/local/include -include-pch /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PrecompiledHeaders/SharedPrecompiledHeaders/12151610001510045616/WebKit2Prefix.h.gch -D _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D NDEBUG -D HAVE_CORE_PREDICTION -D ENABLE_LOWER_FORMATREADERBUNDLE_CODESIGNING_REQUIREMENTS -D WEBKIT_BUNDLE_VERSION="625.1.10+" -D FRAMEWORK_NAME=WebKit -D __clang_analyzer__ -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-own-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-all-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/WebCore.framework/PrivateHeaders/ForwardingHeaders -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebKit -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebKit/IPC -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/pal/graphics/WebGPU -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/pal/graphics/WebGPU -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/WebKitAdditions -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/WebKitAdditions -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webrtc -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webrtc -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webm -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webm -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/DerivedSources-normal/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/DerivedSources/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/DerivedSources -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/System/Library/PrivateFrameworks -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/PrivateFrameworks -D __STDC_WANT_LIB_EXT1__=1 -I/usr/local/include -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include -internal-isystem /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/Frameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/SubFrameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/Library/Frameworks -Wno-trigraphs -Werror -Wno-missing-field-initializers -Wmissing-prototypes -Wunreachable-code -Wnon-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wsign-compare -Wno-shorten-64-to-32 -Wnewline-eof -Wno-c++11-extensions -Wno-implicit-fallthrough -Wdeprecated-declarations -Winvalid-offsetof -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -Wall -Wc99-designator -Wconditional-uninitialized -Wextra -Wdeprecated-enum-enum-conversion -Wdeprecated-enum-float-conversion -Wenum-float-conversion -Wfinal-dtor-non-final-class -Wformat=2 -Wmisleading-indentation -Wpointer-to-int-cast -Wreorder-init-list -Wundef -Wunused-but-set-variable -Wvla -Werror=unguarded-availability -Wno-elaborated-enum-base -Wno-unused-parameter -Wcast-qual -Wchar-subscripts -Wextra-tokens -Winit-self -Wmissing-noreturn -Wpacked -Wpointer-arith -Wredundant-decls -Wwrite-strings -Wexit-time-destructors -Wglobal-constructors -Wtautological-compare -Wimplicit-fallthrough -Wvla -Wliteral-conversion -Wthread-safety -Wno-profile-instr-out-of-date -Wno-profile-instr-unprofiled -Wno-backend-plugin -Wunsafe-buffer-usage -Wno-unknown-warning-option -Wno-missing-field-initializers -Wno-nullability-completeness -std=c++2b -fdeprecated-macro -ferror-limit 19 -fmacro-backtrace-limit=0 -fvisibility=hidden -fvisibility-inlines-hidden -stack-protector 1 -fblocks -fencode-extended-block-signature -fno-rtti -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fno-cxx-modules -fno-implicit-modules -fskip-odr-check-in-gmf -fpascal-strings -fmax-type-align=16 -fdiagnostics-show-note-include-stack -vectorize-loops -vectorize-slp -fsafe-buffer-usage-suggestions -analyzer-output=html -analyzer-config report-in-main-source-file=true -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true -analyzer-checker optin.osx.cocoa.localizability.NonLocalizedStringChecker -analyzer-checker security.insecureAPI.UncheckedReturn -analyzer-checker security.insecureAPI.getpw -analyzer-checker security.insecureAPI.gets -analyzer-checker security.insecureAPI.mkstemp -analyzer-checker security.insecureAPI.mktemp -analyzer-disable-checker security.insecureAPI.rand -analyzer-disable-checker security.insecureAPI.strcpy -analyzer-checker security.insecureAPI.vfork -analyzer-disable-checker alpha,apiModeling,core,cplusplus,deadcode,debug,fuchsia,nullability,optin,osx,security,unix,webkit -analyzer-checker alpha.webkit.ForwardDeclChecker,alpha.webkit.MemoryUnsafeCastChecker,alpha.webkit.NoDeleteChecker,alpha.webkit.NoUncheckedPtrMemberChecker,alpha.webkit.NoUnretainedMemberChecker,alpha.webkit.RetainPtrCtorAdoptChecker,alpha.webkit.UncheckedCallArgsChecker,alpha.webkit.UncheckedLocalVarsChecker,alpha.webkit.UncountedCallArgsChecker,alpha.webkit.UncountedLocalVarsChecker,alpha.webkit.UnretainedCallArgsChecker,alpha.webkit.UnretainedLambdaCapturesChecker,alpha.webkit.UnretainedLocalVarsChecker,webkit.NoUncountedMemberChecker,webkit.RefCntblBaseVirtualDtor,webkit.UncountedLambdaCapturesChecker -analyzer-config max-nodes=10000000 -analyzer-config verbose-report-filename=true -fdwarf2-cfi-asm -o /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebKit/WebKit/normal/arm64/UnifiedSource116.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebKit/unified-sources/UnifiedSource116.cpp
1/*
2 * Copyright (C) 2011-2025 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebFullScreenManager.h"
28
29#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
30
31#include "Connection.h"
32#include "FullScreenMediaDetails.h"
33#include "Logging.h"
34#include "MessageSenderInlines.h"
35#include "WebFrame.h"
36#include "WebFullScreenManagerProxyMessages.h"
37#include "WebPage.h"
38#include <WebCore/AbortSignal.h>
39#include <WebCore/Color.h>
40#include <WebCore/ContainerNodeInlines.h>
41#include <WebCore/DocumentFullscreen.h>
42#include <WebCore/DocumentQuirks.h>
43#include <WebCore/DocumentView.h>
44#include <WebCore/EventNames.h>
45#include <WebCore/FrameInlines.h>
46#include <WebCore/HTMLImageElement.h>
47#include <WebCore/HTMLVideoElement.h>
48#include <WebCore/JSDOMPromiseDeferred.h>
49#include <WebCore/LocalFrame.h>
50#include <WebCore/LocalFrameView.h>
51#include <WebCore/MIMETypeRegistry.h>
52#include <WebCore/NodeDocument.h>
53#include <WebCore/RenderImage.h>
54#include <WebCore/RenderLayerBacking.h>
55#include <WebCore/RenderObjectInlines.h>
56#include <WebCore/RenderView.h>
57#include <WebCore/Settings.h>
58#include <WebCore/TreeScope.h>
59#include <WebCore/TypedElementDescendantIteratorInlines.h>
60#include <WebCore/UserGestureIndicator.h>
61#include <ranges>
62#include <wtf/LoggerHelper.h>
63
64#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
65#include <pal/system/ios/UserInterfaceIdiom.h>
66#endif
67
68#if PLATFORM(IOS_FAMILY)(defined 1 && 1) || (PLATFORM(MAC)(defined WTF_PLATFORM_MAC && WTF_PLATFORM_MAC) && ENABLE(VIDEO_PRESENTATION_MODE)(defined 1 && 1))
69#include "PlaybackSessionManager.h"
70#include "VideoPresentationManager.h"
71#endif
72
73namespace WebKit {
74using namespace WebCore;
75
76using WebCore::FloatSize;
77
78static WebCore::IntRect screenRectOfContents(WebCore::Element& element)
79{
80 CheckedPtr renderer = element.renderer();
81 if (!renderer)
82 return { };
83
84 IntRect contentsRect = renderer->absoluteBoundingBoxRect();
85 if (contentsRect.isEmpty()) {
86 // A zero-height element may contain visible overflow contents. If the element
87 // itself is empty, traverse its children to find its visual content area.
88 LayoutRect topLevelRect;
89 contentsRect = snappedIntRect(renderer->paintingRootRect(topLevelRect));
90 }
91
92 if (contentsRect.isEmpty())
93 return { };
94
95 Ref frameView = renderer->view().frameView();
Call argument for 'this' parameter is unchecked and unsafe
96
97 // The element may have contents which are far outside the viewport of the page.
98 // Clip the contents rect by the current viewport.
99 auto viewportRect = snappedIntRect(frameView->layoutViewportRect());
100 contentsRect.intersect(viewportRect);
101
102 return frameView->contentsToScreen(contentsRect);
103}
104
105Ref<WebFullScreenManager> WebFullScreenManager::create(WebPage& page)
106{
107 return adoptRef(*new WebFullScreenManager(page));
108}
109
110WebFullScreenManager::WebFullScreenManager(WebPage& page)
111 : WebCore::EventListener(WebCore::EventListener::CPPEventListenerType)
112 , m_page(page)
113#if ENABLE(VIDEO)(defined 1 && 1) && ENABLE(IMAGE_ANALYSIS)(defined 1 && 1)
114 , m_mainVideoElementTextRecognitionTimer(RunLoop::mainSingleton(), "WebFullScreenManager::MainVideoElementTextRecognitionTimer"_s, this, &WebFullScreenManager::mainVideoElementTextRecognitionTimerFired)
115#endif
116#if ENABLE(QUICKLOOK_FULLSCREEN)(defined ENABLE_QUICKLOOK_FULLSCREEN && ENABLE_QUICKLOOK_FULLSCREEN
)
117 , m_waitForLargerImageLoadTimer(RunLoop::mainSingleton(), "WebFullScreenManager::WaitForImageLoadTimer"_s, this, &WebFullScreenManager::waitForLargerImageLoadTimerFired)
118#endif
119#if !RELEASE_LOG_DISABLED!((defined 1 && 1) || (defined ENABLE_JOURNALD_LOG &&
ENABLE_JOURNALD_LOG) || (defined WTF_OS_ANDROID && WTF_OS_ANDROID
))
120 , m_logger(page.logger())
121 , m_logIdentifier(page.logIdentifier())
122#endif
123{
124}
125
126WebFullScreenManager::~WebFullScreenManager()
127{
128 invalidate();
129}
130
131void WebFullScreenManager::invalidate()
132{
133 ALWAYS_LOG(LOGIDENTIFIER)Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()))
;
134 clearElement();
135#if ENABLE(VIDEO)(defined 1 && 1)
136 setMainVideoElement(nullptr);
137#if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1)
138 m_mainVideoElementTextRecognitionTimer.stop();
139#endif
140#endif
141 m_pendingWillEnterCallback = nullptr;
142 m_pendingDidEnterCallback = nullptr;
143#if ENABLE(QUICKLOOK_FULLSCREEN)(defined ENABLE_QUICKLOOK_FULLSCREEN && ENABLE_QUICKLOOK_FULLSCREEN
)
144 m_waitForLargerImageLoadTimer.stop();
145 m_waitingForLargerImageLoad = LargerImageLoadState::NotWaiting;
146 m_pendingImageMediaDetails = std::nullopt;
147#endif
148}
149
150WebCore::Element* WebFullScreenManager::element()
151{
152 return m_element.get();
153}
154
155void WebFullScreenManager::videoControlsManagerDidChange()
156{
157#if PLATFORM(IOS_FAMILY)(defined 1 && 1) || (PLATFORM(MAC)(defined WTF_PLATFORM_MAC && WTF_PLATFORM_MAC) && ENABLE(VIDEO_PRESENTATION_MODE)(defined 1 && 1))
158 ALWAYS_LOG(LOGIDENTIFIER)Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()))
;
159
160 if (!m_element) {
161 setPIPStandbyElement(nullptr);
162 return;
163 }
164
165 RefPtr currentPlaybackControlsElement = dynamicDowncast<WebCore::HTMLVideoElement>(protect(m_page->playbackSessionManager())->currentPlaybackControlsElement());
166 if (!currentPlaybackControlsElement) {
167 setPIPStandbyElement(nullptr);
168 return;
169 }
170
171 setPIPStandbyElement(currentPlaybackControlsElement.get());
172#endif
173}
174
175void WebFullScreenManager::setPIPStandbyElement(WebCore::HTMLVideoElement* pipStandbyElement)
176{
177#if ENABLE(VIDEO)(defined 1 && 1)
178 if (pipStandbyElement == m_pipStandbyElement)
179 return;
180
181#if !RELEASE_LOG_DISABLED!((defined 1 && 1) || (defined ENABLE_JOURNALD_LOG &&
ENABLE_JOURNALD_LOG) || (defined WTF_OS_ANDROID && WTF_OS_ANDROID
))
182 auto logIdentifierForElement = [] (auto* element) { return element ? element->logIdentifier() : 0; };
183#endif
184 ALWAYS_LOG(LOGIDENTIFIER, "old element ", logIdentifierForElement(m_pipStandbyElement.get()), ", new element ", logIdentifierForElement(pipStandbyElement))Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "old element ", logIdentifierForElement
(m_pipStandbyElement.get()), ", new element ", logIdentifierForElement
(pipStandbyElement))
;
185
186 if (m_pipStandbyElement)
187 Ref { *m_pipStandbyElement }->setVideoFullscreenStandby(false);
188
189 m_pipStandbyElement = pipStandbyElement;
190
191 if (m_pipStandbyElement)
192 Ref { *m_pipStandbyElement }->setVideoFullscreenStandby(true);
193#endif
194}
195
196bool WebFullScreenManager::supportsFullScreenForElement(const WebCore::Element& element, bool withKeyboard)
197{
198 if (!m_page->corePage()->isDocumentFullscreenEnabled())
199 return false;
200
201 if (m_inWindowFullScreenMode && &element != m_element)
202 return false;
203
204#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
205 return PAL::currentUserInterfaceIdiomIsDesktop() || !withKeyboard;
206#else
207 return true;
208#endif
209}
210
211static auto& eventsToObserve()
212{
213 static NeverDestroyed eventsToObserve = std::array {
214 WebCore::eventNames().playEvent,
215 WebCore::eventNames().pauseEvent,
216 WebCore::eventNames().loadedmetadataEvent,
217 };
218 return eventsToObserve.get();
219}
220
221void WebFullScreenManager::setElement(WebCore::Element& element)
222{
223 if (m_element == &element)
224 return;
225
226 clearElement();
227
228 m_element = element;
229 m_elementToRestore = element;
230 m_elementFrameIdentifier = element.document().frame()->frameID();
231
232 for (auto& eventName : eventsToObserve())
233 element.addEventListener(eventName, *this, { { true }, std::nullopt, false, nullptr, false });
234}
235
236void WebFullScreenManager::clearElement()
237{
238 RefPtr element = m_element;
239 if (!element)
240 return;
241
242 for (auto& eventName : eventsToObserve())
243 element->removeEventListener(eventName, *this, { true });
244
245 m_element = nullptr;
246 m_elementFrameIdentifier = std::nullopt;
247}
248
249#if ENABLE(QUICKLOOK_FULLSCREEN)(defined ENABLE_QUICKLOOK_FULLSCREEN && ENABLE_QUICKLOOK_FULLSCREEN
)
250FullScreenMediaDetails WebFullScreenManager::getImageMediaDetails(CheckedPtr<RenderImage> renderImage, IsUpdating updating)
251{
252 if (!renderImage)
253 return { };
254
255 auto* cachedImage = renderImage->cachedImage();
256 if (!cachedImage || cachedImage->errorOccurred())
257 return { };
258
259 RefPtr image = cachedImage->image();
260 if (!image)
261 return { };
262 if (!(image->shouldUseQuickLookForFullscreen() || updating == IsUpdating::Yes))
263 return { };
264
265 auto* buffer = cachedImage->resourceBuffer();
266 if (!buffer)
267 return { };
268
269 auto imageSize = image->size();
270
271 auto mimeType = image->mimeType();
272 if (!MIMETypeRegistry::isSupportedImageMIMEType(mimeType))
273 mimeType = MIMETypeRegistry::mimeTypeForExtension(image->filenameExtension());
274 if (!MIMETypeRegistry::isSupportedImageMIMEType(mimeType))
275 mimeType = MIMETypeRegistry::mimeTypeForPath(cachedImage->url().string());
276 if (!MIMETypeRegistry::isSupportedImageMIMEType(mimeType))
277 return { };
278
279 auto sharedMemoryBuffer = SharedMemory::copyBuffer(*buffer);
280 if (!sharedMemoryBuffer)
281 return { };
282
283 auto imageResourceHandle = sharedMemoryBuffer->createHandle(SharedMemory::Protection::ReadOnly);
284
285 m_willUseQuickLookForFullscreen = true;
286
287 return {
288 FullScreenMediaDetails::Type::Image,
289 imageSize,
290 mimeType,
291 imageResourceHandle
292 };
293}
294#endif // ENABLE(QUICKLOOK_FULLSCREEN)
295
296void WebFullScreenManager::enterFullScreenForElement(Element& element, HTMLMediaElementEnums::VideoFullscreenMode mode, CompletionHandler<void(ExceptionOr<void>)>&& willEnterFullScreenCallback, CompletionHandler<bool(bool)>&& didEnterFullScreenCallback)
297{
298 ALWAYS_LOG(LOGIDENTIFIER, "<", element.tagName(), " id=\"", element.getIdAttribute(), "\">")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "<", element.
tagName(), " id=\"", element.getIdAttribute(), "\">")
;
299
300 setElement(element);
301
302#if PLATFORM(IOS_FAMILY)(defined 1 && 1) || (PLATFORM(MAC)(defined WTF_PLATFORM_MAC && WTF_PLATFORM_MAC) && ENABLE(VIDEO_PRESENTATION_MODE)(defined 1 && 1))
303 if (m_page->videoPresentationManager().videoElementInPictureInPicture() && protect(m_element->document())->quirks().blocksEnteringStandardFullscreenFromPictureInPictureQuirk()) {
304 willEnterFullScreenCallback(Exception { ExceptionCode::NotAllowedError });
305 didEnterFullScreenCallback(false);
306 return;
307 }
308
309 if (RefPtr currentPlaybackControlsElement = protect(m_page->playbackSessionManager())->currentPlaybackControlsElement())
310 currentPlaybackControlsElement->prepareForVideoFullscreenStandby();
311#endif
312
313 m_pendingWillEnterCallback = WTF::move(willEnterFullScreenCallback);
314 m_pendingDidEnterCallback = WTF::move(didEnterFullScreenCallback);
315 m_pendingMode = mode;
316#if ENABLE(QUICKLOOK_FULLSCREEN)(defined ENABLE_QUICKLOOK_FULLSCREEN && ENABLE_QUICKLOOK_FULLSCREEN
)
317 m_pendingImageMediaDetails = std::nullopt;
318#endif
319
320#if ENABLE(QUICKLOOK_FULLSCREEN)(defined ENABLE_QUICKLOOK_FULLSCREEN && ENABLE_QUICKLOOK_FULLSCREEN
)
321 if (CheckedPtr renderImage = dynamicDowncast<RenderImage>(element.renderer())) {
322 auto* cachedImage = renderImage->cachedImage();
323 RefPtr image = cachedImage ? cachedImage->image() : nullptr;
324
325 if (image && image->isSpatial()) {
326 m_page->freezeLayerTree(WebPage::LayerTreeFreezeReason::OutOfProcessFullscreen);
327 m_pendingImageMediaDetails = getImageMediaDetails(renderImage, IsUpdating::No);
328 m_pendingImageMediaDetails->launchInImmersive = false;
329 m_willUseQuickLookForFullscreen = true;
330 performEnterFullScreen();
331 return;
332 }
333 if (image && image->isMaybePanoramic()) {
334 // Check if responsive image might provide a larger source
335 RefPtr imageElement = dynamicDowncast<HTMLImageElement>(&element);
336 if (imageElement)
337 m_imageSourceBeforeViewportChange = imageElement->currentURL();
338
339 m_waitingForLargerImageLoad = LargerImageLoadState::WaitingForLoad;
340
341 m_page->freezeLayerTree(WebPage::LayerTreeFreezeReason::OutOfProcessFullscreen);
342 static constexpr auto maxViewportSize = FloatSize { 10000, 10000 };
343 m_oldSize = m_page->viewportConfiguration().viewLayoutSize();
344 m_scaleFactor = m_page->viewportConfiguration().layoutSizeScaleFactor();
345 m_minEffectiveWidth = m_page->viewportConfiguration().minimumEffectiveDeviceWidth();
346 m_page->setViewportConfigurationViewLayoutSize(maxViewportSize, m_scaleFactor, m_minEffectiveWidth);
347 element.document().updateLayoutIgnorePendingStylesheets();
348
349 URL newURL;
350 if (imageElement)
351 newURL = imageElement->currentURL();
352 bool sourceChanged = !m_imageSourceBeforeViewportChange.isEmpty()
353 && m_imageSourceBeforeViewportChange != newURL;
354
355 if (sourceChanged) {
356 // Wait for new image to load (up to 1 second)
357 // performEnterFullScreen will be called by updateImageSource or timer (if timed out)
358 m_waitForLargerImageLoadTimer.startOneShot(1_s);
359 return;
360 }
361
362 // Source did NOT change, no need to wait anymore
363 m_waitingForLargerImageLoad = LargerImageLoadState::NotWaiting;
364
365 cachedImage = renderImage->cachedImage();
366 image = cachedImage ? cachedImage->image() : nullptr;
367
368 bool isPanorama = image && image->isPanorama();
369 if (isPanorama) {
370 m_pendingImageMediaDetails = getImageMediaDetails(renderImage, IsUpdating::No);
371 m_pendingImageMediaDetails->launchInImmersive = true;
372 m_willUseQuickLookForFullscreen = true;
373 } else {
374 m_page->setViewportConfigurationViewLayoutSize(m_oldSize, m_scaleFactor, m_minEffectiveWidth);
375 m_page->unfreezeLayerTree(WebPage::LayerTreeFreezeReason::OutOfProcessFullscreen);
376 m_willUseQuickLookForFullscreen = false;
377 }
378 performEnterFullScreen();
379 return;
380 }
381 }
382#endif
383
384 performEnterFullScreen();
385}
386
387void WebFullScreenManager::performEnterFullScreen()
388{
389 RefPtr element = m_element;
390 if (!element) {
391 if (m_pendingWillEnterCallback)
392 m_pendingWillEnterCallback(Exception { ExceptionCode::InvalidStateError });
393 if (m_pendingDidEnterCallback)
394 m_pendingDidEnterCallback(false);
395 m_pendingWillEnterCallback = nullptr;
396 m_pendingDidEnterCallback = nullptr;
397 return;
398 }
399
400#if ENABLE(QUICKLOOK_FULLSCREEN)(defined ENABLE_QUICKLOOK_FULLSCREEN && ENABLE_QUICKLOOK_FULLSCREEN
)
401 // If we were waiting for larger image load (async path), re-evaluate and decide what to do
402 if (m_waitingForLargerImageLoad != LargerImageLoadState::NotWaiting) {
403 bool timedOut = m_waitingForLargerImageLoad == LargerImageLoadState::TimedOut;
404 m_waitingForLargerImageLoad = LargerImageLoadState::NotWaiting;
405
406 CheckedPtr renderImage = dynamicDowncast<RenderImage>(element->renderer());
407 if (!renderImage) {
408 m_page->setViewportConfigurationViewLayoutSize(m_oldSize, m_scaleFactor, m_minEffectiveWidth);
409 m_page->unfreezeLayerTree(WebPage::LayerTreeFreezeReason::OutOfProcessFullscreen);
410 m_willUseQuickLookForFullscreen = false;
411 } else {
412 auto* cachedImage = renderImage->cachedImage();
413 RefPtr image = cachedImage ? cachedImage->image() : nullptr;
414 bool isPanorama = image && image->isPanorama();
415
416 if (!timedOut && !isPanorama) {
417 // Loaded in time but doesn't qualify. Restore viewport and use regular fullscreen
418 m_page->setViewportConfigurationViewLayoutSize(m_oldSize, m_scaleFactor, m_minEffectiveWidth);
419 m_page->unfreezeLayerTree(WebPage::LayerTreeFreezeReason::OutOfProcessFullscreen);
420 m_willUseQuickLookForFullscreen = false;
421 } else {
422 // Either timed out OR loaded a panoramic image in time
423 m_pendingImageMediaDetails = getImageMediaDetails(renderImage, IsUpdating::No);
424 m_pendingImageMediaDetails->launchInImmersive = isPanorama;
425 m_willUseQuickLookForFullscreen = true;
426 }
427 }
428 }
429#endif // ENABLE(QUICKLOOK_FULLSCREEN)
430
431 FullScreenMediaDetails mediaDetails;
432#if ENABLE(QUICKLOOK_FULLSCREEN)(defined ENABLE_QUICKLOOK_FULLSCREEN && ENABLE_QUICKLOOK_FULLSCREEN
)
433 if (m_pendingImageMediaDetails) {
434 mediaDetails = WTF::move(*m_pendingImageMediaDetails);
435 m_pendingImageMediaDetails = std::nullopt;
436 }
437#endif
438
439 m_initialFrame = screenRectOfContents(*element);
440
441#if ENABLE(VIDEO)(defined 1 && 1)
442 updateMainVideoElement();
443
444#if ENABLE(VIDEO_USES_ELEMENT_FULLSCREEN)(defined ENABLE_VIDEO_USES_ELEMENT_FULLSCREEN && ENABLE_VIDEO_USES_ELEMENT_FULLSCREEN
)
445 if (RefPtr mainVideoElement = m_mainVideoElement.get()) {
446 bool fullscreenElementIsVideoElement = is<HTMLVideoElement>(*element);
447
448 auto mainVideoElementSize = [&]() -> FloatSize {
449#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
450 if (!fullscreenElementIsVideoElement && protect(element->document())->quirks().shouldDisableFullscreenVideoAspectRatioAdaptiveSizing())
451 return { };
452#endif
453 return FloatSize(mainVideoElement->videoWidth(), mainVideoElement->videoHeight());
454 }();
455
456 mediaDetails = {
457 fullscreenElementIsVideoElement ? FullScreenMediaDetails::Type::Video : FullScreenMediaDetails::Type::ElementWithVideo,
458 mainVideoElementSize
459 };
460 }
461#endif // ENABLE(VIDEO_USES_ELEMENT_FULLSCREEN)
462
463 m_page->prepareToEnterElementFullScreen();
464
465 if (RefPtr page = m_page->corePage()) {
466 if (RefPtr view = protect(page->mainFrame())->virtualView())
467 m_scrollPosition = view->scrollPosition();
468 }
469
470 if (m_pendingMode == HTMLMediaElementEnums::VideoFullscreenModeInWindow) {
471 willEnterFullScreen(*element, WTF::move(m_pendingWillEnterCallback), WTF::move(m_pendingDidEnterCallback), m_pendingMode);
472 m_inWindowFullScreenMode = true;
473 } else {
474 ASSERT(m_elementFrameIdentifier)((void)0);
475 m_page->sendWithAsyncReply(Messages::WebFullScreenManagerProxy::EnterFullScreen(*m_elementFrameIdentifier, protect(m_element->document())->quirks().blocksReturnToFullscreenFromPictureInPictureQuirk(), WTF::move(mediaDetails)), [
476 this,
477 protectedThis = Ref { *this },
478 element = Ref { *element },
479 willEnterFullScreenCallback = WTF::move(m_pendingWillEnterCallback),
480 didEnterFullScreenCallback = WTF::move(m_pendingDidEnterCallback)
481 ] (bool success) mutable {
482 if (success) {
483 willEnterFullScreen(element, WTF::move(willEnterFullScreenCallback), WTF::move(didEnterFullScreenCallback));
484 return;
485 }
486 willEnterFullScreenCallback(Exception { ExceptionCode::InvalidStateError });
487 didEnterFullScreenCallback(false);
488 });
489 }
490#endif
491}
492
493#if ENABLE(QUICKLOOK_FULLSCREEN)(defined ENABLE_QUICKLOOK_FULLSCREEN && ENABLE_QUICKLOOK_FULLSCREEN
)
494void WebFullScreenManager::updateImageSource(WebCore::Element& element)
495{
496 if (&element != m_element)
497 return;
498
499 if (m_waitingForLargerImageLoad == LargerImageLoadState::WaitingForLoad) {
500 m_waitForLargerImageLoadTimer.stop();
501 performEnterFullScreen();
502 } else {
503 // We're already in QuickLook Fullscreen, update the displayed image
504 FullScreenMediaDetails mediaDetails;
505 CheckedPtr renderImage = dynamicDowncast<RenderImage>(element.renderer());
506 if (renderImage && m_willUseQuickLookForFullscreen) {
507 mediaDetails = getImageMediaDetails(renderImage, IsUpdating::Yes);
508 m_page->send(Messages::WebFullScreenManagerProxy::UpdateImageSource(WTF::move(mediaDetails)));
509 }
510 }
511}
512
513void WebFullScreenManager::waitForLargerImageLoadTimerFired()
514{
515 if (m_waitingForLargerImageLoad == LargerImageLoadState::NotWaiting)
516 return;
517
518 m_waitingForLargerImageLoad = LargerImageLoadState::TimedOut;
519 performEnterFullScreen();
520}
521#endif // ENABLE(QUICKLOOK_FULLSCREEN)
522
523void WebFullScreenManager::exitFullScreenForElement(WebCore::Element* element, CompletionHandler<void()>&& completionHandler)
524{
525 if (element)
526 ALWAYS_LOG(LOGIDENTIFIER, "<", element->tagName(), " id=\"", element->getIdAttribute(), "\">")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "<", element->
tagName(), " id=\"", element->getIdAttribute(), "\">")
;
527 else
528 ALWAYS_LOG(LOGIDENTIFIER, "null")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "null")
;
529
530 m_page->prepareToExitElementFullScreen();
531
532 if (m_inWindowFullScreenMode) {
533 willExitFullScreen([this, protectedThis = Ref { *this }, completionHandler = WTF::move(completionHandler)] mutable {
534 didExitFullScreen(WTF::move(completionHandler));
535 m_inWindowFullScreenMode = false;
536 });
537 } else {
538 m_page->sendWithAsyncReply(Messages::WebFullScreenManagerProxy::ExitFullScreen(), [this, protectedThis = Ref { *this }, completionHandler = WTF::move(completionHandler)] mutable {
539 willExitFullScreen(WTF::move(completionHandler));
540 });
541 }
542#if ENABLE(VIDEO)(defined 1 && 1)
543 setMainVideoElement(nullptr);
544#endif
545}
546
547void WebFullScreenManager::willEnterFullScreen(Element& element, CompletionHandler<void(ExceptionOr<void>)>&& willEnterFullscreenCallback, CompletionHandler<bool(bool)>&& didEnterFullscreenCallback, WebCore::HTMLMediaElementEnums::VideoFullscreenMode mode)
548{
549 ALWAYS_LOG(LOGIDENTIFIER, "<", element.tagName(), " id=\"", element.getIdAttribute(), "\">")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "<", element.
tagName(), " id=\"", element.getIdAttribute(), "\">")
;
550
551 m_page->isInFullscreenChanged(WebPage::IsInFullscreenMode::Yes);
552
553 auto result = protect(protect(element.document())->fullscreen())->willEnterFullscreen(element, mode);
554 if (result.hasException())
555 close();
556 willEnterFullscreenCallback(result);
557
558#if !PLATFORM(IOS_FAMILY)(defined 1 && 1)
559 m_page->hidePageBanners();
560#endif
561 protect(element.document())->updateLayout();
562 m_finalFrame = screenRectOfContents(element);
563
564 m_page->sendWithAsyncReply(Messages::WebFullScreenManagerProxy::BeganEnterFullScreen(m_initialFrame, m_finalFrame), [this, protectedThis = Ref { *this }, mode, completionHandler = WTF::move(didEnterFullscreenCallback)] (bool success) mutable {
565 if (!success && mode != WebCore::HTMLMediaElementEnums::VideoFullscreenModeInWindow) {
566 completionHandler(false);
567 return;
568 }
569 didEnterFullScreen(WTF::move(completionHandler));
570 });
571}
572
573void WebFullScreenManager::didEnterFullScreen(CompletionHandler<bool(bool)>&& completionHandler)
574{
575 RefPtr element = m_element;
576 if (!element) {
577 completionHandler(false);
578 return;
579 }
580
581 ALWAYS_LOG(LOGIDENTIFIER, "<", element->tagName(), " id=\"", element->getIdAttribute(), "\">")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "<", element->
tagName(), " id=\"", element->getIdAttribute(), "\">")
;
582
583 if (!completionHandler(true)) {
584 close();
585 return;
586 }
587
588#if PLATFORM(IOS_FAMILY)(defined 1 && 1) || (PLATFORM(MAC)(defined WTF_PLATFORM_MAC && WTF_PLATFORM_MAC) && ENABLE(VIDEO_PRESENTATION_MODE)(defined 1 && 1))
589 RefPtr currentPlaybackControlsElement = protect(m_page->playbackSessionManager())->currentPlaybackControlsElement();
590 setPIPStandbyElement(dynamicDowncast<WebCore::HTMLVideoElement>(currentPlaybackControlsElement.get()));
591#endif
592
593#if ENABLE(VIDEO)(defined 1 && 1)
594 updateMainVideoElement();
595#endif
596}
597
598#if ENABLE(VIDEO)(defined 1 && 1)
599
600void WebFullScreenManager::updateMainVideoElement()
601{
602 setMainVideoElement([&]() -> RefPtr<WebCore::HTMLVideoElement> {
603 if (!m_element)
604 return nullptr;
605
606 if (auto video = dynamicDowncast<WebCore::HTMLVideoElement>(*m_element))
607 return video;
608
609 RefPtr<WebCore::HTMLVideoElement> mainVideo;
610 WebCore::FloatRect mainVideoBounds;
611 for (Ref video : WebCore::descendantsOfType<WebCore::HTMLVideoElement>(*m_element)) {
612 auto rendererAndBounds = video->boundingAbsoluteRectWithoutLayout();
613 if (!rendererAndBounds)
614 continue;
615
616 auto& [renderer, bounds] = *rendererAndBounds;
617 if (!renderer || bounds.isEmpty())
618 continue;
619
620 if (bounds.area() <= mainVideoBounds.area())
621 continue;
622
623 mainVideoBounds = bounds;
624 mainVideo = WTF::move(video);
625 }
626 return mainVideo;
627 }());
628}
629
630#endif // ENABLE(VIDEO)
631
632void WebFullScreenManager::willExitFullScreen(CompletionHandler<void()>&& completionHandler)
633{
634 RefPtr element = m_element;
635 if (!element || !m_elementFrameIdentifier)
636 return completionHandler();
637 ALWAYS_LOG(LOGIDENTIFIER, "<", element->tagName(), " id=\"", element->getIdAttribute(), "\">")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "<", element->
tagName(), " id=\"", element->getIdAttribute(), "\">")
;
638
639#if ENABLE(VIDEO)(defined 1 && 1)
640 setPIPStandbyElement(nullptr);
641#endif
642
643 m_finalFrame = screenRectOfContents(*element);
644 if (!protect(protect(element->document())->fullscreen())->willExitFullscreen()) {
645 close();
646 return completionHandler();
647 }
648#if !PLATFORM(IOS_FAMILY)(defined 1 && 1)
649 m_page->showPageBanners();
650#endif
651 // FIXME: The order of these frames is switched, but that is kept for historical reasons.
652 // It should probably be fixed to be consistent at some point.
653 m_page->sendWithAsyncReply(Messages::WebFullScreenManagerProxy::BeganExitFullScreen(*m_elementFrameIdentifier, m_finalFrame, m_initialFrame), [this, protectedThis = Ref { *this }, completionHandler = WTF::move(completionHandler)] mutable {
654 didExitFullScreen(WTF::move(completionHandler));
655 });
656}
657
658static Vector<Ref<Element>> collectFullscreenElementsFromElement(Element* rawElement)
659{
660 if (!rawElement)
661 return { };
662
663 RefPtr document = rawElement->document();
664
665 if (rawElement != protect(document->fullscreen())->fullscreenElement())
666 return { };
667
668 RefPtr element = rawElement;
669
670 Vector<Ref<Element>> fullscreenElements;
671
672 while (true) {
673 fullscreenElements.append(element.releaseNonNull());
674
675 document = document->parentDocument();
676 if (!document)
677 break;
678
679 element = protect(document->fullscreen())->fullscreenElement();
680 if (!element)
681 break;
682 }
683
684 return fullscreenElements;
685}
686
687void WebFullScreenManager::didExitFullScreen(CompletionHandler<void()>&& completionHandler)
688{
689#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION) && ENABLE(QUICKLOOK_FULLSCREEN)(defined ENABLE_QUICKLOOK_FULLSCREEN && ENABLE_QUICKLOOK_FULLSCREEN
)
690 if (std::exchange(m_willUseQuickLookForFullscreen, false)) {
691 m_page->setViewportConfigurationViewLayoutSize(m_oldSize, m_scaleFactor, m_minEffectiveWidth);
692 m_page->unfreezeLayerTree(WebPage::LayerTreeFreezeReason::OutOfProcessFullscreen);
693 }
694#endif
695
696 m_page->isInFullscreenChanged(WebPage::IsInFullscreenMode::No);
697
698 RefPtr element = m_element;
699 if (!element)
700 return completionHandler();
701
702 ALWAYS_LOG(LOGIDENTIFIER, "<", element->tagName(), " id=\"", element->getIdAttribute(), "\">")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "<", element->
tagName(), " id=\"", element->getIdAttribute(), "\">")
;
703
704 setFullscreenInsets(WebCore::FloatBoxExtent());
705 setFullscreenAutoHideDuration(0_s);
706
707 auto fullscreenElements = collectFullscreenElementsFromElement(element.get());
708
709 completionHandler();
710
711 // Ensure the element (and all its parent fullscreen elements) that just exited fullscreen are still in view:
712 while (!fullscreenElements.isEmpty()) {
713 auto fullscreenElement = fullscreenElements.takeLast();
714 fullscreenElement->scrollIntoViewIfNotVisible(true, AllowScrollingOverflowHidden::No);
715 }
716
717 clearElement();
718
719 if (RefPtr localMainFrame = m_page->corePage()->localMainFrame()) {
720 // Make sure overflow: hidden is unapplied from the root element before restoring.
721 RefPtr view = localMainFrame->view();
722 view->forceLayout();
723 view->setScrollPosition(m_scrollPosition);
724 }
725}
726
727void WebFullScreenManager::setAnimatingFullScreen(bool animating)
728{
729 if (!m_element)
730 return;
731 protect(protect(m_element->document())->fullscreen())->setAnimatingFullscreen(animating);
732}
733
734void WebFullScreenManager::requestRestoreFullScreen(CompletionHandler<void(bool)>&& completionHandler)
735{
736 ASSERT(!m_element)((void)0);
737 if (m_element)
738 return completionHandler(false);
739
740 auto element = RefPtr { m_elementToRestore.get() };
741 if (!element) {
742 ALWAYS_LOG(LOGIDENTIFIER, "no element to restore")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "no element to restore"
)
;
743 return completionHandler(false);
744 }
745
746 ALWAYS_LOG(LOGIDENTIFIER, "<", element->tagName(), " id=\"", element->getIdAttribute(), "\">")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "<", element->
tagName(), " id=\"", element->getIdAttribute(), "\">")
;
747 WebCore::UserGestureIndicator gestureIndicator(WebCore::IsProcessingUserGesture::Yes, &element->document());
748 protect(protect(element->document())->fullscreen())->requestFullscreen(*element, WebCore::DocumentFullscreen::ExemptIFrameAllowFullscreenRequirement, [completionHandler = WTF::move(completionHandler)] (auto result) mutable {
749 completionHandler(!result.hasException());
750 });
751}
752
753void WebFullScreenManager::requestExitFullScreen()
754{
755 if (!m_element) {
756 ALWAYS_LOG(LOGIDENTIFIER, "no element, closing")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "no element, closing"
)
;
757 close();
758 return;
759 }
760
761 RefPtr localMainFrame = m_page->localMainFrame();
762 RefPtr topDocument = localMainFrame ? localMainFrame->document() : nullptr;
763 if (!topDocument || !protect(topDocument->fullscreen())->fullscreenElement()) {
764 ALWAYS_LOG(LOGIDENTIFIER, "top document not in fullscreen, closing")Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()), "top document not in fullscreen, closing"
)
;
765 close();
766 return;
767 }
768
769 ALWAYS_LOG(LOGIDENTIFIER)Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()))
;
770 protect(protect(m_element->document())->fullscreen())->fullyExitFullscreen();
771}
772
773void WebFullScreenManager::close()
774{
775 if (m_closing)
776 return;
777 m_closing = true;
778 ALWAYS_LOG(LOGIDENTIFIER)Ref { logger() }->logAlways(logChannel(), WTF::Logger::LogSiteIdentifier
(logClassName(), __func__, logIdentifier()))
;
779 m_page->closeFullScreen();
780 invalidate();
781 m_closing = false;
782}
783
784void WebFullScreenManager::setFullscreenInsets(const WebCore::FloatBoxExtent& insets)
785{
786 protect(m_page->corePage())->setFullscreenInsets(insets);
787}
788
789void WebFullScreenManager::setFullscreenAutoHideDuration(Seconds duration)
790{
791 protect(m_page->corePage())->setFullscreenAutoHideDuration(duration);
792}
793
794void WebFullScreenManager::handleEvent(WebCore::ScriptExecutionContext& context, WebCore::Event& event)
795{
796#if ENABLE(VIDEO)(defined 1 && 1)
797 RefPtr targetElement = dynamicDowncast<WebCore::Element>(event.currentTarget());
798 if (!m_element || !targetElement)
799 return;
800
801 Ref document = m_element->document();
802 if (&context != document.ptr() || !protect(document->fullscreen())->isFullscreen())
803 return;
804
805 if (targetElement == m_element) {
806 updateMainVideoElement();
807 return;
808 }
809
810#if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1)
811 if (targetElement == m_mainVideoElement.get()) {
812 auto& targetVideoElement = downcast<WebCore::HTMLVideoElement>(*targetElement);
813 if (targetVideoElement.paused() && !targetVideoElement.seeking())
814 scheduleTextRecognitionForMainVideo();
815 else
816 endTextRecognitionForMainVideoIfNeeded();
817 }
818#endif
819#else
820 UNUSED_PARAM(event)(void)event;
821 UNUSED_PARAM(context)(void)context;
822#endif
823}
824
825#if ENABLE(VIDEO)(defined 1 && 1)
826
827#if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1)
828void WebFullScreenManager::mainVideoElementTextRecognitionTimerFired()
829{
830 if (!m_element || !protect(protect(m_element->document())->fullscreen())->isFullscreen())
831 return;
832
833 updateMainVideoElement();
834
835 RefPtr mainVideoElement = m_mainVideoElement.get();
836 if (!mainVideoElement)
837 return;
838
839 if (m_isPerformingTextRecognitionInMainVideo)
840 m_page->cancelTextRecognitionForVideoInElementFullScreen();
841
842 m_isPerformingTextRecognitionInMainVideo = true;
843 m_page->beginTextRecognitionForVideoInElementFullScreen(*mainVideoElement);
844}
845
846void WebFullScreenManager::scheduleTextRecognitionForMainVideo()
847{
848 m_mainVideoElementTextRecognitionTimer.startOneShot(250_ms);
849}
850
851void WebFullScreenManager::endTextRecognitionForMainVideoIfNeeded()
852{
853 m_mainVideoElementTextRecognitionTimer.stop();
854
855 if (m_isPerformingTextRecognitionInMainVideo) {
856 m_page->cancelTextRecognitionForVideoInElementFullScreen();
857 m_isPerformingTextRecognitionInMainVideo = false;
858 }
859}
860#endif // ENABLE(IMAGE_ANALYSIS)
861
862void WebFullScreenManager::setMainVideoElement(RefPtr<WebCore::HTMLVideoElement>&& element)
863{
864 if (element == m_mainVideoElement.get())
865 return;
866
867 static NeverDestroyed eventsToObserve = std::array {
868 WebCore::eventNames().seekingEvent,
869 WebCore::eventNames().seekedEvent,
870 WebCore::eventNames().playingEvent,
871 WebCore::eventNames().pauseEvent,
872 };
873
874 if (RefPtr mainVideoElement = m_mainVideoElement.get()) {
875 for (auto& eventName : eventsToObserve.get())
876 mainVideoElement->removeEventListener(eventName, *this, { });
877
878#if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1)
879 endTextRecognitionForMainVideoIfNeeded();
880#endif
881 }
882
883 m_mainVideoElement = element;
884
885 if (element) {
886 for (auto& eventName : eventsToObserve.get())
887 element->addEventListener(eventName, *this);
888
889#if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1)
890 if (element->paused())
891 scheduleTextRecognitionForMainVideo();
892#endif
893 }
894}
895
896#endif // ENABLE(VIDEO)
897
898#if !RELEASE_LOG_DISABLED!((defined 1 && 1) || (defined ENABLE_JOURNALD_LOG &&
ENABLE_JOURNALD_LOG) || (defined WTF_OS_ANDROID && WTF_OS_ANDROID
))
899WTFLogChannel& WebFullScreenManager::logChannel() const
900{
901 return WebKit2LogFullscreen;
902}
903#endif
904
905void WebFullScreenManager::enterFullScreenForOwnerElements(WebCore::FrameIdentifier frameID, CompletionHandler<void()>&& completionHandler)
906{
907 RefPtr webFrame = WebFrame::webFrame(frameID);
908 if (!webFrame)
909 return completionHandler();
910 RefPtr coreFrame = webFrame->coreFrame();
911 if (!coreFrame)
912 return completionHandler();
913
914 Vector<Ref<Element>> elements;
915 for (RefPtr frame = coreFrame; frame; frame = frame->tree().parent()) {
916 if (RefPtr element = frame->ownerElement())
917 elements.append(element.releaseNonNull());
918 }
919 for (auto element : elements | std::views::reverse)
920 DocumentFullscreen::elementEnterFullscreen(element);
921
922 completionHandler();
923}
924
925void WebFullScreenManager::exitFullScreenInMainFrame(CompletionHandler<void()>&& completionHandler)
926{
927 RefPtr mainFrame = m_page->mainFrame();
928 if (!mainFrame)
929 return completionHandler();
930
931 DocumentFullscreen::finishExitFullscreen(*mainFrame, DocumentFullscreen::ExitMode::Resize);
932 completionHandler();
933}
934
935} // namespace WebKit
936
937#endif // ENABLE(FULLSCREEN_API)