| File: | Source/WebKit/WebProcess/FullScreen/WebFullScreenManager.cpp |
| Warning: | line 95, column 21 Call argument for 'this' parameter is unchecked and unsafe |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 | |
| 73 | namespace WebKit { |
| 74 | using namespace WebCore; |
| 75 | |
| 76 | using WebCore::FloatSize; |
| 77 | |
| 78 | static 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 | |
| 105 | Ref<WebFullScreenManager> WebFullScreenManager::create(WebPage& page) |
| 106 | { |
| 107 | return adoptRef(*new WebFullScreenManager(page)); |
| 108 | } |
| 109 | |
| 110 | WebFullScreenManager::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 | |
| 126 | WebFullScreenManager::~WebFullScreenManager() |
| 127 | { |
| 128 | invalidate(); |
| 129 | } |
| 130 | |
| 131 | void 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 | |
| 150 | WebCore::Element* WebFullScreenManager::element() |
| 151 | { |
| 152 | return m_element.get(); |
| 153 | } |
| 154 | |
| 155 | void 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 | |
| 175 | void 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 | |
| 196 | bool 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 | |
| 211 | static 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 | |
| 221 | void 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 | |
| 236 | void 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 ) |
| 250 | FullScreenMediaDetails 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 | |
| 296 | void 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 | |
| 387 | void 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 ) |
| 494 | void 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 | |
| 513 | void 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 | |
| 523 | void 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 | |
| 547 | void 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 | |
| 573 | void 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 | |
| 600 | void 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 | |
| 632 | void 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 | |
| 658 | static 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 | |
| 687 | void 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 | |
| 727 | void WebFullScreenManager::setAnimatingFullScreen(bool animating) |
| 728 | { |
| 729 | if (!m_element) |
| 730 | return; |
| 731 | protect(protect(m_element->document())->fullscreen())->setAnimatingFullscreen(animating); |
| 732 | } |
| 733 | |
| 734 | void 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 | |
| 753 | void 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 | |
| 773 | void 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 | |
| 784 | void WebFullScreenManager::setFullscreenInsets(const WebCore::FloatBoxExtent& insets) |
| 785 | { |
| 786 | protect(m_page->corePage())->setFullscreenInsets(insets); |
| 787 | } |
| 788 | |
| 789 | void WebFullScreenManager::setFullscreenAutoHideDuration(Seconds duration) |
| 790 | { |
| 791 | protect(m_page->corePage())->setFullscreenAutoHideDuration(duration); |
| 792 | } |
| 793 | |
| 794 | void 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) |
| 828 | void 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 | |
| 846 | void WebFullScreenManager::scheduleTextRecognitionForMainVideo() |
| 847 | { |
| 848 | m_mainVideoElementTextRecognitionTimer.startOneShot(250_ms); |
| 849 | } |
| 850 | |
| 851 | void 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 | |
| 862 | void 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 )) |
| 899 | WTFLogChannel& WebFullScreenManager::logChannel() const |
| 900 | { |
| 901 | return WebKit2LogFullscreen; |
| 902 | } |
| 903 | #endif |
| 904 | |
| 905 | void 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 | |
| 925 | void 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) |