Commit bdc54e4b authored by philipj@opera.com's avatar philipj@opera.com

Extract "fullscreen element ready check" from requestFullscreen()

http://fullscreen.spec.whatwg.org/#fullscreen-element-ready-check

This is a change in behavior for what was previously "a descendant
browsing context's document has a non-empty fullscreen element stack,"
now "element has no ancestor element whose local name is iframe and
namespace is the HTML namespace."

It's now never possible to go fullscreen with a descendant of an iframe,
whereas previously it would be possible as long as that iframe's
document had an empty fullscreen element stack.

Since the difference is very unlikely to affect any real content, the
LegacyFullScreenErrorExemption was dropped for this case. There was
apparently no test coverage for that exemption.

BUG=402376

Review URL: https://codereview.chromium.org/462103002

git-svn-id: svn://svn.chromium.org/blink/trunk@180162 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 5732b0d9
<!DOCTYPE html>
<title>Element ready check with enabled flag not set</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../trusted-event.js"></script>
<div id="log"></div>
<iframe></iframe>
<script>
async_test(function(t)
{
var iframe = document.querySelector("iframe");
document.onfullscreenchange = t.unreached_func("document fullscreenchange event");
document.onfullscreenerror = t.unreached_func("document fullscreenerror event");
iframe.contentDocument.onfullscreenchange = t.unreached_func("iframe fullscreenchange event");
iframe.contentDocument.onfullscreenerror = t.step_func_done();
assert_false(iframe.contentDocument.fullscreenEnabled, "fullscreen enabled flag");
trusted_request(iframe.contentDocument.body, document.body);
});
</script>
<!DOCTYPE html>
<title>Element ready check for sibling of fullscreen element</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../trusted-event.js"></script>
<div id="log"></div>
<div id="a"></div>
<div id="b"></div>
<script>
async_test(function(t)
{
var a = document.getElementById("a");
var b = document.getElementById("b");
document.onfullscreenchange = t.step_func(function()
{
assert_equals(document.fullscreenElement, a, "fullscreen element");
trusted_request(b);
document.onfullscreenchange = t.unreached_func("second fullscreenchange event");
document.onfullscreenerror = t.step_func_done();
});
trusted_request(a);
});
</script>
<!DOCTYPE html>
<title>Element ready check for child of a fullscreen iframe</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../trusted-event.js"></script>
<div id="log"></div>
<iframe><!-- script inserts div here --></iframe>
<script>
// Verify that an iframe can itself go fullscreen, and that this doesn't
// influence the iframe ancestor test of the element ready check.
async_test(function(t)
{
var iframe = document.querySelector("iframe");
document.onfullscreenchange = t.step_func(function()
{
assert_equals(document.fullscreenElement, iframe, "fullscreen element");
var div = document.createElement("div");
// This adds the div to the iframe element itself, not to the iframe's
// contentDocument. It's done here because the HTML parser treats the
// content of iframe as a text node.
iframe.appendChild(div);
trusted_request(div, iframe.contentDocument.body);
document.onfullscreenchange = t.unreached_func("second fullscreenchange event");
document.onfullscreenerror = t.step_func_done();
});
trusted_request(iframe);
});
</script>
<!DOCTYPE html>
<title>Element ready check for child of iframe</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../trusted-event.js"></script>
<div id="log"></div>
<iframe><!-- script inserts child here --></iframe>
<script>
async_test(function(t)
{
var div = document.createElement("div");
document.querySelector("iframe").appendChild(div);
document.onfullscreenchange = t.unreached_func("fullscreenchange event");
document.onfullscreenerror = t.step_func_done();
trusted_request(div, document.body);
});
</script>
<!DOCTYPE html>
<title>Element ready check for element not in a document</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../trusted-event.js"></script>
<div id="log"></div>
<script>
async_test(function(t)
{
var div = document.createElement("div");
document.onfullscreenchange = t.unreached_func("fullscreenchange event");
document.onfullscreenerror = t.step_func_done();
trusted_request(div, document.body);
});
</script>
...@@ -17,6 +17,7 @@ function trusted_event(callback, container) ...@@ -17,6 +17,7 @@ function trusted_event(callback, container)
// Running as manual test. Show a button to click. // Running as manual test. Show a button to click.
var button = document.createElement("button"); var button = document.createElement("button");
button.textContent = "click to run test"; button.textContent = "click to run test";
button.style.display = "block";
button.style.fontSize = "20px"; button.style.fontSize = "20px";
button.style.padding = "10px"; button.style.padding = "10px";
button.onclick = function() button.onclick = function()
...@@ -30,8 +31,10 @@ function trusted_event(callback, container) ...@@ -30,8 +31,10 @@ function trusted_event(callback, container)
} }
// Invokes element.requestFullscreen() from a trusted event. // Invokes element.requestFullscreen() from a trusted event.
function trusted_request(element) // When testing manually, a button is added to the container,
// or to element's parent if no container is provided.
function trusted_request(element, container)
{ {
var request = element.requestFullscreen.bind(element); var request = element.requestFullscreen.bind(element);
trusted_event(request, element.parentNode); trusted_event(request, container || element.parentNode);
} }
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#include "core/frame/LocalFrame.h" #include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h" #include "core/frame/Settings.h"
#include "core/frame/UseCounter.h" #include "core/frame/UseCounter.h"
#include "core/html/HTMLFrameOwnerElement.h" #include "core/html/HTMLIFrameElement.h"
#include "core/html/HTMLMediaElement.h" #include "core/html/HTMLMediaElement.h"
#include "core/page/Chrome.h" #include "core/page/Chrome.h"
#include "core/page/ChromeClient.h" #include "core/page/ChromeClient.h"
...@@ -169,64 +169,57 @@ void FullscreenElementStack::documentWasDisposed() ...@@ -169,64 +169,57 @@ void FullscreenElementStack::documentWasDisposed()
} }
#endif #endif
bool FullscreenElementStack::elementReady(Element& element, RequestType requestType)
{
// A fullscreen element ready check for an element returns true if all of the following are
// true, and false otherwise:
// element is in a document.
if (!element.inDocument())
return false;
// element's node document's fullscreen enabled flag is set.
if (!fullscreenIsAllowedForAllOwners(element.document())) {
if (requestType == PrefixedVideoRequest)
UseCounter::count(element.document(), UseCounter::VideoFullscreenAllowedExemption);
else
return false;
}
// element's node document fullscreen element stack is either empty or its top element is an
// ancestor of element.
if (Element* lastElementOnStack = fullscreenElementFrom(element.document())) {
if (!element.isDescendantOf(lastElementOnStack)) {
if (requestType == PrefixedMozillaRequest || requestType == PrefixedMozillaAllowKeyboardInputRequest)
UseCounter::count(element.document(), UseCounter::LegacyFullScreenErrorExemption);
else
return false;
}
}
// element has no ancestor element whose local name is iframe and namespace is the HTML
// namespace.
if (Traversal<HTMLIFrameElement>::firstAncestor(element))
return false;
return true;
}
void FullscreenElementStack::requestFullscreen(Element& element, RequestType requestType) void FullscreenElementStack::requestFullscreen(Element& element, RequestType requestType)
{ {
// Ignore this request if the document is not in a live frame. // Ignore this request if the document is not in a live frame.
if (!document()->isActive()) if (!document()->isActive())
return; return;
// The Mozilla Full Screen API <https://wiki.mozilla.org/Gecko:FullScreenAPI> has different requirements
// for full screen mode, and do not have the concept of a full screen element stack.
bool inLegacyMozillaMode = requestType == PrefixedMozillaRequest || requestType == PrefixedMozillaAllowKeyboardInputRequest;
do { do {
// 1. If any of the following conditions are true, terminate these steps and queue a task to fire // 1. If any of the following conditions are true, terminate these steps and queue a task to fire
// an event named fullscreenerror with its bubbles attribute set to true on the context object's // an event named fullscreenerror with its bubbles attribute set to true on the context object's
// node document: // node document:
// The context object is not in a document. // The fullscreen element ready check returns false.
if (!element.inDocument()) if (!elementReady(element, requestType))
break; break;
// The context object's node document, or an ancestor browsing context's document does not have
// the fullscreen enabled flag set.
if (!fullscreenIsAllowedForAllOwners(element.document())) {
if (requestType == PrefixedVideoRequest)
UseCounter::count(element.document(), UseCounter::VideoFullscreenAllowedExemption);
else
break;
}
// The context object's node document fullscreen element stack is not empty and its top element
// is not an ancestor of the context object. (NOTE: Ignore this requirement if the request was
// made via the legacy Mozilla-style API.)
if (Element* lastElementOnStack = fullscreenElement()) {
if (!element.isDescendantOf(lastElementOnStack)) {
if (inLegacyMozillaMode)
UseCounter::count(element.document(), UseCounter::LegacyFullScreenErrorExemption);
else
break;
}
}
// A descendant browsing context's document has a non-empty fullscreen element stack.
bool descendentHasNonEmptyStack = false;
for (Frame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) {
if (!descendant->isLocalFrame())
continue;
ASSERT(toLocalFrame(descendant)->document());
if (fullscreenElementFrom(*toLocalFrame(descendant)->document())) {
descendentHasNonEmptyStack = true;
break;
}
}
if (descendentHasNonEmptyStack) {
if (inLegacyMozillaMode)
UseCounter::count(element.document(), UseCounter::LegacyFullScreenErrorExemption);
else
break;
}
// This algorithm is not allowed to show a pop-up: // This algorithm is not allowed to show a pop-up:
// An algorithm is allowed to show a pop-up if, in the task in which the algorithm is running, either: // An algorithm is allowed to show a pop-up if, in the task in which the algorithm is running, either:
// - an activation behavior is currently being processed whose click event was trusted, or // - an activation behavior is currently being processed whose click event was trusted, or
......
...@@ -103,6 +103,8 @@ private: ...@@ -103,6 +103,8 @@ private:
Document* document(); Document* document();
static bool elementReady(Element&, RequestType);
void clearFullscreenElementStack(); void clearFullscreenElementStack();
void popFullscreenElementStack(); void popFullscreenElementStack();
void pushFullscreenElementStack(Element&, RequestType); void pushFullscreenElementStack(Element&, RequestType);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment