Issue #2135 - Bug 1430305: Implement ShadowRoot.fullscreenElement

This commit is contained in:
FranklinDM 2023-03-03 19:52:43 +08:00 committed by roytam1
commit e286eb8adb
10 changed files with 105 additions and 40 deletions

View file

@ -160,5 +160,25 @@ DocumentOrShadowRoot::GetPointerLockElement()
retargetedPointerLockedElement->AsElement() : nullptr; retargetedPointerLockedElement->AsElement() : nullptr;
} }
Element*
DocumentOrShadowRoot::GetFullscreenElement()
{
if (!AsNode().IsInComposedDoc()) {
return nullptr;
}
Element* element = AsNode().OwnerDoc()->FullScreenStackTop();
NS_ASSERTION(!element ||
element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
"Fullscreen element should have fullscreen styles applied");
nsIContent* retargeted = Retarget(element);
if (retargeted && retargeted->IsElement()) {
return retargeted->AsElement();
}
return nullptr;
}
} }
} }

View file

@ -115,6 +115,7 @@ public:
~DocumentOrShadowRoot() = default; ~DocumentOrShadowRoot() = default;
Element* GetPointerLockElement(); Element* GetPointerLockElement();
Element* GetFullscreenElement();
protected: protected:
nsIContent* Retarget(nsIContent* aContent) const; nsIContent* Retarget(nsIContent* aContent) const;

View file

@ -8603,7 +8603,7 @@ nsDocument::OnPageHide(bool aPersisted,
EnumerateActivityObservers(NotifyActivityChanged, nullptr); EnumerateActivityObservers(NotifyActivityChanged, nullptr);
ClearPendingFullscreenRequests(this); ClearPendingFullscreenRequests(this);
if (GetFullscreenElement()) { if (FullScreenStackTop()) {
// If this document was fullscreen, we should exit fullscreen in this // If this document was fullscreen, we should exit fullscreen in this
// doctree branch. This ensures that if the user navigates while in // doctree branch. This ensures that if the user navigates while in
// fullscreen mode we don't leave its still visible ancestor documents // fullscreen mode we don't leave its still visible ancestor documents
@ -10307,7 +10307,7 @@ nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
static bool static bool
CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData) CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
{ {
if (aDoc->GetFullscreenElement()) { if (aDoc->FullScreenStackTop()) {
uint32_t* count = static_cast<uint32_t*>(aData); uint32_t* count = static_cast<uint32_t*>(aData);
(*count)++; (*count)++;
} }
@ -10327,7 +10327,7 @@ nsDocument::IsFullscreenLeaf()
{ {
// A fullscreen leaf document is fullscreen, and has no fullscreen // A fullscreen leaf document is fullscreen, and has no fullscreen
// subdocuments. // subdocuments.
if (!GetFullscreenElement()) { if (!FullScreenStackTop()) {
return false; return false;
} }
return CountFullscreenSubDocuments(this) == 0; return CountFullscreenSubDocuments(this) == 0;
@ -10336,11 +10336,11 @@ nsDocument::IsFullscreenLeaf()
static bool static bool
ResetFullScreen(nsIDocument* aDocument, void* aData) ResetFullScreen(nsIDocument* aDocument, void* aData)
{ {
if (aDocument->GetFullscreenElement()) { if (aDocument->FullScreenStackTop()) {
NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1, NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
"Should have at most 1 fullscreen subdocument."); "Should have at most 1 fullscreen subdocument.");
static_cast<nsDocument*>(aDocument)->CleanupFullscreenState(); static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
NS_ASSERTION(!aDocument->GetFullscreenElement(), NS_ASSERTION(!aDocument->FullScreenStackTop(),
"Should reset full-screen"); "Should reset full-screen");
auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData); auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData);
changed->AppendElement(aDocument); changed->AppendElement(aDocument);
@ -10388,7 +10388,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
UnlockPointer(); UnlockPointer();
nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot(); nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
if (!root || !root->GetFullscreenElement()) { if (!root || !root->FullScreenStackTop()) {
// If a document was detached before exiting from fullscreen, it is // If a document was detached before exiting from fullscreen, it is
// possible that the root had left fullscreen state. In this case, // possible that the root had left fullscreen state. In this case,
// we would not get anything from the ResetFullScreen() call. Root's // we would not get anything from the ResetFullScreen() call. Root's
@ -10417,7 +10417,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
DispatchFullScreenChange(changed[changed.Length() - i - 1]); DispatchFullScreenChange(changed[changed.Length() - i - 1]);
} }
NS_ASSERTION(!root->GetFullscreenElement(), NS_ASSERTION(!root->FullScreenStackTop(),
"Fullscreen root should no longer be a fullscreen doc..."); "Fullscreen root should no longer be a fullscreen doc...");
// Move the top-level window out of fullscreen mode. // Move the top-level window out of fullscreen mode.
@ -10434,7 +10434,7 @@ GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
nsIDocument** result = static_cast<nsIDocument**>(aData); nsIDocument** result = static_cast<nsIDocument**>(aData);
*result = aDoc; *result = aDoc;
return false; return false;
} else if (aDoc->GetFullscreenElement()) { } else if (aDoc->FullScreenStackTop()) {
aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData); aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
} }
return true; return true;
@ -10453,7 +10453,7 @@ GetFullscreenLeaf(nsIDocument* aDoc)
nsIDocument* root = nsContentUtils::GetRootDocument(aDoc); nsIDocument* root = nsContentUtils::GetRootDocument(aDoc);
// Check that the root is actually fullscreen so we don't waste time walking // Check that the root is actually fullscreen so we don't waste time walking
// around its descendants. // around its descendants.
if (!root->GetFullscreenElement()) { if (!root->FullScreenStackTop()) {
return nullptr; return nullptr;
} }
GetFullscreenLeaf(root, &leaf); GetFullscreenLeaf(root, &leaf);
@ -10463,10 +10463,10 @@ GetFullscreenLeaf(nsIDocument* aDoc)
void void
nsDocument::RestorePreviousFullScreenState() nsDocument::RestorePreviousFullScreenState()
{ {
NS_ASSERTION(!GetFullscreenElement() || !FullscreenRoots::IsEmpty(), NS_ASSERTION(!FullScreenStackTop() || !FullscreenRoots::IsEmpty(),
"Should have at least 1 fullscreen root when fullscreen!"); "Should have at least 1 fullscreen root when fullscreen!");
if (!GetFullscreenElement() || !GetWindow() || FullscreenRoots::IsEmpty()) { if (!FullScreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) {
return; return;
} }
@ -10641,7 +10641,7 @@ nsDocument::FullScreenStackPush(Element* aElement)
} }
EventStateManager::SetFullScreenState(aElement, true); EventStateManager::SetFullScreenState(aElement, true);
mFullScreenStack.AppendElement(do_GetWeakReference(aElement)); mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
NS_ASSERTION(GetFullscreenElement() == aElement, "Should match"); NS_ASSERTION(FullScreenStackTop() == aElement, "Should match");
UpdateViewportScrollbarOverrideForFullscreen(this); UpdateViewportScrollbarOverrideForFullscreen(this);
return true; return true;
} }
@ -10689,7 +10689,7 @@ nsDocument::FullScreenStackTop()
uint32_t last = mFullScreenStack.Length() - 1; uint32_t last = mFullScreenStack.Length() - 1;
nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last])); nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
NS_ASSERTION(element, "Should have full-screen element!"); NS_ASSERTION(element, "Should have full-screen element!");
NS_ASSERTION(element->IsInUncomposedDoc(), "Full-screen element should be in doc"); NS_ASSERTION(element->IsInComposedDoc(), "Full-screen element should be in doc");
NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc"); NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
return element; return element;
} }
@ -10767,7 +10767,7 @@ nsresult nsDocument::RemoteFrameFullscreenReverted()
} }
/* static */ bool /* static */ bool
nsDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject) nsIDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
return nsContentUtils::IsCallerChrome() || return nsContentUtils::IsCallerChrome() ||
@ -10815,10 +10815,10 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
{ {
NS_ASSERTION(aElement, NS_ASSERTION(aElement,
"Must pass non-null element to nsDocument::RequestFullScreen"); "Must pass non-null element to nsDocument::RequestFullScreen");
if (!aElement || aElement == GetFullscreenElement()) { if (!aElement || aElement == FullScreenStackTop()) {
return false; return false;
} }
if (!aElement->IsInUncomposedDoc()) { if (!aElement->IsInComposedDoc()) {
DispatchFullscreenError("FullscreenDeniedNotInDocument"); DispatchFullscreenError("FullscreenDeniedNotInDocument");
return false; return false;
} }
@ -10842,8 +10842,11 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
DispatchFullscreenError("FullscreenDeniedSubDocFullScreen"); DispatchFullscreenError("FullscreenDeniedSubDocFullScreen");
return false; return false;
} }
if (GetFullscreenElement() && //XXXsmaug Note, we don't follow the latest fullscreen spec here.
!nsContentUtils::ContentIsDescendantOf(aElement, GetFullscreenElement())) { // This whole check could be probably removed.
if (FullScreenStackTop() &&
!nsContentUtils::ContentIsHostIncludingDescendantOf(aElement,
FullScreenStackTop())) {
// If this document is full-screen, only grant full-screen requests from // If this document is full-screen, only grant full-screen requests from
// a descendant of the current full-screen element. // a descendant of the current full-screen element.
DispatchFullscreenError("FullscreenDeniedNotDescendant"); DispatchFullscreenError("FullscreenDeniedNotDescendant");
@ -11203,16 +11206,6 @@ nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement)
return NS_OK; return NS_OK;
} }
Element*
nsDocument::GetFullscreenElement()
{
Element* element = FullScreenStackTop();
NS_ASSERTION(!element ||
element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
"Fullscreen element should have fullscreen styles applied");
return element;
}
NS_IMETHODIMP NS_IMETHODIMP
nsDocument::GetMozFullScreen(bool *aFullScreen) nsDocument::GetMozFullScreen(bool *aFullScreen)
{ {

View file

@ -896,8 +896,6 @@ public:
// //
already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const; already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const;
static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
// Do the "fullscreen element ready check" from the fullscreen spec. // Do the "fullscreen element ready check" from the fullscreen spec.
// It returns true if the given element is allowed to go into fullscreen. // It returns true if the given element is allowed to go into fullscreen.
bool FullscreenElementReadyCheck(Element* aElement, bool aWasCallerChrome); bool FullscreenElementReadyCheck(Element* aElement, bool aWasCallerChrome);
@ -921,11 +919,10 @@ public:
void FullScreenStackPop(); void FullScreenStackPop();
// Returns the top element from the full-screen stack. // Returns the top element from the full-screen stack.
Element* FullScreenStackTop(); Element* FullScreenStackTop() override;
// DOM-exposed fullscreen API // DOM-exposed fullscreen API
bool FullscreenEnabled() override; bool FullscreenEnabled() override;
Element* GetFullscreenElement() override;
void RequestPointerLock(Element* aElement) override; void RequestPointerLock(Element* aElement) override;
bool SetPointerLock(Element* aElement, int aCursorStyle); bool SetPointerLock(Element* aElement, int aCursorStyle);

View file

@ -2637,7 +2637,7 @@ public:
nsIURI* GetDocumentURIObject() const; nsIURI* GetDocumentURIObject() const;
// Not const because all the full-screen goop is not const // Not const because all the full-screen goop is not const
virtual bool FullscreenEnabled() = 0; virtual bool FullscreenEnabled() = 0;
virtual Element* GetFullscreenElement() = 0; virtual Element* FullScreenStackTop() = 0;
bool Fullscreen() bool Fullscreen()
{ {
return !!GetFullscreenElement(); return !!GetFullscreenElement();
@ -2647,6 +2647,7 @@ public:
{ {
UnlockPointer(this); UnlockPointer(this);
} }
static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
#ifdef MOZILLA_INTERNAL_API #ifdef MOZILLA_INTERNAL_API
bool Hidden() const bool Hidden() const
{ {

View file

@ -0,0 +1,52 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1430305
Bug 1430305 - Implement ShadowRoot.fullscreenElement
-->
<head>
<title>Bug 1430305</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
</script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js">
</script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1430305">
Mozilla Bug 1430305</a>
<div id="host"></div>
<pre id="test">
<script type="application/javascript">
function begin() {
var host = document.getElementById("host");
var shadowRoot = host.attachShadow({mode: "open"});
shadowRoot.innerHTML = "<div>test</div>";
var elem = shadowRoot.firstChild;
var gotFullscreenEvent = false;
document.addEventListener("fullscreenchange", function (e) {
if (document.fullscreenElement === host) {
is(shadowRoot.fullscreenElement, elem,
"Expected element entered fullsceen");
gotFullscreenEvent = true;
document.exitFullscreen();
} else {
opener.ok(gotFullscreenEvent, "Entered fullscreen as expected");
is(shadowRoot.fullscreenElement, null,
"Shouldn't have fullscreenElement anymore.");
is(document.fullscreenElement, null,
"Shouldn't have fullscreenElement anymore.");
opener.nextTest();
}
});
elem.requestFullscreen();
}
</script>
</pre>
</body>
</html>

View file

@ -72,6 +72,7 @@ support-files =
file_fullscreen-rollback.html file_fullscreen-rollback.html
file_fullscreen-scrollbar.html file_fullscreen-scrollbar.html
file_fullscreen-selector.html file_fullscreen-selector.html
file_fullscreen-shadowdom.html
file_fullscreen-svg-element.html file_fullscreen-svg-element.html
file_fullscreen-top-layer.html file_fullscreen-top-layer.html
file_fullscreen-unprefix-disabled-inner.html file_fullscreen-unprefix-disabled-inner.html

View file

@ -39,6 +39,7 @@ var gTestWindows = [
"file_fullscreen-navigation.html", "file_fullscreen-navigation.html",
"file_fullscreen-scrollbar.html", "file_fullscreen-scrollbar.html",
"file_fullscreen-selector.html", "file_fullscreen-selector.html",
"file_fullscreen-shadowdom.html",
"file_fullscreen-top-layer.html", "file_fullscreen-top-layer.html",
"file_fullscreen-backdrop.html", "file_fullscreen-backdrop.html",
"file_fullscreen-nested.html", "file_fullscreen-nested.html",
@ -96,7 +97,8 @@ addLoadEvent(function() {
["full-screen-api.unprefix.enabled", true], ["full-screen-api.unprefix.enabled", true],
["full-screen-api.allow-trusted-requests-only", false], ["full-screen-api.allow-trusted-requests-only", false],
["full-screen-api.transition-duration.enter", "0 0"], ["full-screen-api.transition-duration.enter", "0 0"],
["full-screen-api.transition-duration.leave", "0 0"] ["full-screen-api.transition-duration.leave", "0 0"],
["dom.webcomponents.enabled", true]
]}, nextTest); ]}, nextTest);
}); });
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();

View file

@ -238,10 +238,6 @@ partial interface Document {
readonly attribute boolean fullscreenEnabled; readonly attribute boolean fullscreenEnabled;
[BinaryName="fullscreenEnabled"] [BinaryName="fullscreenEnabled"]
readonly attribute boolean mozFullScreenEnabled; readonly attribute boolean mozFullScreenEnabled;
[LenientSetter, Func="nsDocument::IsUnprefixedFullscreenEnabled"]
readonly attribute Element? fullscreenElement;
[BinaryName="fullscreenElement"]
readonly attribute Element? mozFullScreenElement;
[Func="nsDocument::IsUnprefixedFullscreenEnabled"] [Func="nsDocument::IsUnprefixedFullscreenEnabled"]
void exitFullscreen(); void exitFullscreen();

View file

@ -23,6 +23,8 @@ interface DocumentOrShadowRoot {
readonly attribute StyleSheetList styleSheets; readonly attribute StyleSheetList styleSheets;
readonly attribute Element? pointerLockElement; readonly attribute Element? pointerLockElement;
// Not implemented yet: bug 1430305. [LenientSetter, Func="nsDocument::IsUnprefixedFullscreenEnabled"]
// readonly attribute Element? fullscreenElement; readonly attribute Element? fullscreenElement;
[BinaryName="fullscreenElement"]
readonly attribute Element? mozFullScreenElement;
}; };