Skip to content
Permalink
Browse files
Properly resolve fullscreen API promises
https://bugs.webkit.org/show_bug.cgi?id=248879
rdar://103074395

Reviewed by Chris Dumez.

- exitFullscreen() promises should resolve in FullscreenManager::finishExitFullscreen().
- requestFullscreen() promises should resolve in FullscreenManager::willEnterFullscreen().

Store a pendingPromise field for async operations, and ensure there's only one promise at once, by rejecting the previous operation if it is still pending
(meaning the previous operation failed, since it is overriden by the new one).

We resolve this pending promise at the same time as emitting the fullscreen change/error events.

Removed mac-wk1 baselines that were due to this bug.

* LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/css/selectors/modal-pseudo-class-expected.txt: Removed.
* LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/fullscreen/api/document-exit-fullscreen-nested-shadow-dom-expected.txt: Removed.
* LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/fullscreen/api/element-request-fullscreen-same-element-expected.txt: Removed.
* Source/WebCore/dom/DocumentFullscreen.cpp:
(WebCore::DocumentFullscreen::exitFullscreen):
(WebCore::DocumentFullscreen::webkitExitFullscreen):
* Source/WebCore/dom/DocumentFullscreen.h:
(WebCore::DocumentFullscreen::webkitExitFullscreen): Deleted.
* Source/WebCore/dom/FullscreenManager.cpp:
(WebCore::FullscreenManager::requestFullscreenForElement):
(WebCore::FullscreenManager::exitFullscreen):
(WebCore::FullscreenManager::willEnterFullscreen):
(WebCore::FullscreenManager::didExitFullscreen): Since 257456@main, cancelFullscreen now shares codepath with exitFullscreen, making the exitingDocument variable not needed.
(WebCore::FullscreenManager::notifyAboutFullscreenChangeOrError):
(WebCore::FullscreenManager::exitRemovedFullscreenElementIfNeeded):
(WebCore::FullscreenManager::clear):
(WebCore::FullscreenManager::dispatchFullscreenChangeEvents): Deleted.
* Source/WebCore/dom/FullscreenManager.h:
* Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDocumentGtk.cpp:
(webkit_dom_document_webkit_exit_fullscreen):

Canonical link: https://commits.webkit.org/257668@main
  • Loading branch information
nt1m committed Dec 10, 2022
1 parent 94dbfb8 commit 9696329f7af6ec156b6f45ae41d811da8fa0eeee
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 53 deletions.

This file was deleted.

This file was deleted.

This file was deleted.

@@ -34,18 +34,19 @@

namespace WebCore {

// FIXME: Add proper support.
// https://fullscreen.spec.whatwg.org/#exit-fullscreen
void DocumentFullscreen::exitFullscreen(Document& document, Ref<DeferredPromise>&& promise)
void DocumentFullscreen::exitFullscreen(Document& document, RefPtr<DeferredPromise>&& promise)
{
if (!document.isFullyActive() || !document.fullscreenManager().fullscreenElement()) {
promise->reject(Exception { TypeError, "Not in fullscreen"_s });
return;
}
document.fullscreenManager().exitFullscreen();
document.eventLoop().queueTask(TaskSource::MediaElement, [promise = WTFMove(promise)] {
promise->resolve();
});
document.fullscreenManager().exitFullscreen(WTFMove(promise));
}

void DocumentFullscreen::webkitExitFullscreen(Document& document)
{
document.fullscreenManager().exitFullscreen(nullptr);
}

// https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled
@@ -39,12 +39,12 @@ class Element;

class DocumentFullscreen {
public:
static void exitFullscreen(Document&, Ref<DeferredPromise>&&);
static void exitFullscreen(Document&, RefPtr<DeferredPromise>&&);
static bool fullscreenEnabled(Document&);

static bool webkitFullscreenEnabled(Document& document) { return document.fullscreenManager().isFullscreenEnabled(); }
static Element* webkitFullscreenElement(Document& document) { return document.ancestorElementInThisScope(document.fullscreenManager().fullscreenElement()); }
static void webkitExitFullscreen(Document& document) { document.fullscreenManager().exitFullscreen(); }
WEBCORE_EXPORT static void webkitExitFullscreen(Document&);
static bool webkitIsFullScreen(Document& document) { return document.fullscreenManager().isFullscreen(); }
static bool webkitFullScreenKeyboardInputAllowed(Document& document) { return document.fullscreenManager().isFullscreenKeyboardInputAllowed(); }
static Element* webkitCurrentFullScreenElement(Document& document) { return document.ancestorElementInThisScope(document.fullscreenManager().currentFullscreenElement()); }
@@ -87,7 +87,7 @@ void FullscreenManager::requestFullscreenForElement(Ref<Element>&& element, RefP
promise->reject(Exception { TypeError });
m_document.eventLoop().queueTask(TaskSource::MediaElement, [weakThis = WTFMove(weakThis)]() mutable {
if (weakThis)
weakThis->dispatchFullscreenChangeEvents();
weakThis->notifyAboutFullscreenChangeOrError();
});
};

@@ -237,10 +237,13 @@ void FullscreenManager::requestFullscreenForElement(Ref<Element>&& element, RefP
failedPreflights(WTFMove(element), WTFMove(promise));
return;
}
if (m_pendingPromise)
m_pendingPromise->reject(Exception { TypeError, "Pending operation cancelled by requestFullscreen() call."_s });

m_pendingPromise = WTFMove(promise);

INFO_LOG(identifier, "task - success");
page->chrome().client().enterFullScreenForElement(element);
if (promise)
promise->resolve();
});

// 7. Optionally, display a message indicating how the user can exit displaying the context object fullscreen.
@@ -259,6 +262,10 @@ void FullscreenManager::cancelFullscreen()
// there is a pending task in enterFullscreen(). Cause it to cancel and fire an error
// by clearing the pending fullscreen element.
m_pendingFullscreenElement = nullptr;
if (m_pendingPromise) {
m_pendingPromise->reject(Exception { TypeError, "Pending operation cancelled by webkitCancelFullScreen() call."_s });
m_pendingPromise = nullptr;
}
INFO_LOG(LOGIDENTIFIER, "Cancelling pending fullscreen request.");
return;
}
@@ -301,7 +308,7 @@ static Vector<Ref<Document>> documentsToUnfullscreen(Document& firstDocument)
return documents;
}

void FullscreenManager::exitFullscreen()
void FullscreenManager::exitFullscreen(RefPtr<DeferredPromise>&& promise)
{
INFO_LOG(LOGIDENTIFIER);

@@ -325,13 +332,16 @@ void FullscreenManager::exitFullscreen()
m_pendingExitFullscreen = true;

// Return promise, and run the remaining steps in parallel.
m_document.eventLoop().queueTask(TaskSource::MediaElement, [this, weakThis = WeakPtr { *this }, mode, identifier = LOGIDENTIFIER] {
if (!weakThis)
m_document.eventLoop().queueTask(TaskSource::MediaElement, [this, promise = WTFMove(promise), weakThis = WeakPtr { *this }, mode, identifier = LOGIDENTIFIER] () mutable {
if (!weakThis) {
promise->resolve();
return;
}

auto* page = this->page();
if (!page) {
m_pendingExitFullscreen = false;
promise->resolve();
ERROR_LOG(identifier, "task - Document not in page; bailing.");
return;
}
@@ -343,9 +353,15 @@ void FullscreenManager::exitFullscreen()
INFO_LOG(identifier, "task - Cancelling pending fullscreen request.");
m_pendingFullscreenElement = nullptr;
m_pendingExitFullscreen = false;
promise->resolve();
return;
}

if (m_pendingPromise)
m_pendingPromise->reject(Exception { TypeError, "Pending operation cancelled by exitFullscreen() call."_s });

m_pendingPromise = WTFMove(promise);

// Notify the chrome of the new full screen element.
if (mode == ExitMode::Resize)
page->chrome().client().exitFullScreenForElement(m_fullscreenElement.get());
@@ -458,7 +474,7 @@ bool FullscreenManager::willEnterFullscreen(Element& element)
if (is<HTMLIFrameElement>(element))
element.setIFrameFullscreenFlag(true);

dispatchFullscreenChangeEvents();
notifyAboutFullscreenChangeOrError();

return true;
}
@@ -527,16 +543,11 @@ bool FullscreenManager::didExitFullscreen()

document().scheduleFullStyleRebuild();

// When webkitCancelFullscreen is called, we call webkitExitFullscreen on the topDocument(). That
// means that the events will be queued there. So if we have no events here, start the timer on
// the exiting document.
bool eventTargetQueuesEmpty = m_fullscreenChangeEventTargetQueue.isEmpty() && m_fullscreenErrorEventTargetQueue.isEmpty();
Document& exitingDocument = eventTargetQueuesEmpty ? topDocument() : document();
exitingDocument.fullscreenManager().dispatchFullscreenChangeEvents();
notifyAboutFullscreenChangeOrError();
return true;
}

void FullscreenManager::dispatchFullscreenChangeEvents()
void FullscreenManager::notifyAboutFullscreenChangeOrError()
{
// Since we dispatch events in this function, it's possible that the
// document will be detached and GC'd. We protect it here to make sure we
@@ -546,6 +557,16 @@ void FullscreenManager::dispatchFullscreenChangeEvents()
m_fullscreenChangeEventTargetQueue.swap(changeQueue);
Deque<GCReachableRef<Node>> errorQueue;
m_fullscreenErrorEventTargetQueue.swap(errorQueue);

if (m_pendingPromise) {
ASSERT(!errorQueue.isEmpty() || !changeQueue.isEmpty());
if (!errorQueue.isEmpty())
m_pendingPromise->reject(Exception { TypeError });
else
m_pendingPromise->resolve();
m_pendingPromise = nullptr;
}

dispatchFullscreenChangeOrErrorEvent(changeQueue, EventType::Change, /* shouldNotifyMediaElement */ true);
dispatchFullscreenChangeOrErrorEvent(errorQueue, EventType::Error, /* shouldNotifyMediaElement */ false);
}
@@ -598,7 +619,7 @@ void FullscreenManager::exitRemovedFullscreenElementIfNeeded(Element& element)
auto fullscreenElement = fullscreenOrPendingElement();
if (fullscreenElement == &element) {
INFO_LOG(LOGIDENTIFIER, "Fullscreen element removed; exiting fullscreen");
exitFullscreen();
exitFullscreen(nullptr);
} else
element.setFullscreenFlag(false);
}
@@ -643,6 +664,7 @@ void FullscreenManager::clear()
{
m_fullscreenElement = nullptr;
m_pendingFullscreenElement = nullptr;
m_pendingPromise = nullptr;
}

void FullscreenManager::emptyEventQueue()
@@ -57,7 +57,7 @@ class FullscreenManager final : public CanMakeWeakPtr<FullscreenManager> {
// WHATWG Fullscreen API
WEBCORE_EXPORT Element* fullscreenElement() const;
WEBCORE_EXPORT bool isFullscreenEnabled() const;
WEBCORE_EXPORT void exitFullscreen();
WEBCORE_EXPORT void exitFullscreen(RefPtr<DeferredPromise>&&);

// Mozilla versions.
bool isFullscreen() const { return m_fullscreenElement.get(); }
@@ -76,7 +76,7 @@ class FullscreenManager final : public CanMakeWeakPtr<FullscreenManager> {
WEBCORE_EXPORT bool willExitFullscreen();
WEBCORE_EXPORT bool didExitFullscreen();

void dispatchFullscreenChangeEvents();
void notifyAboutFullscreenChangeOrError();

enum class ExitMode : bool { Resize, NoResize };
void finishExitFullscreen(Document&, ExitMode);
@@ -112,6 +112,8 @@ class FullscreenManager final : public CanMakeWeakPtr<FullscreenManager> {

RefPtr<Element> fullscreenOrPendingElement() const { return m_fullscreenElement ? m_fullscreenElement : m_pendingFullscreenElement; }

RefPtr<DeferredPromise> m_pendingPromise;

bool m_pendingExitFullscreen { false };
RefPtr<Element> m_pendingFullscreenElement;
RefPtr<Element> m_fullscreenElement;
@@ -1320,7 +1320,7 @@ void webkit_dom_document_webkit_exit_fullscreen(WebKitDOMDocument* self)
g_return_if_fail(WEBKIT_DOM_IS_DOCUMENT(self));
#if ENABLE(FULLSCREEN_API)
WebCore::Document* item = WebKit::core(self);
item->fullscreenManager().exitFullscreen();
item->fullscreenManager().exitFullscreen(nullptr);
#endif
}

0 comments on commit 9696329

Please sign in to comment.