LibWeb: Improve scrollingElement handling
This change is currently entirely undetectable because of what the added FIXME talks about. Currently, the HTML element's overflow is always set to visible in both axes, so it getting set to "clip" in the imported test ends up not mattering at all.
This commit is contained in:
parent
03f262f03d
commit
fe2bc2bfe7
5 changed files with 177 additions and 12 deletions
|
|
@ -1357,6 +1357,7 @@ static void propagate_overflow_to_viewport(Element& root_element, Layout::Viewpo
|
|||
viewport_computed_values.set_overflow_y(overflow_y_to_apply);
|
||||
|
||||
// The element from which the value is propagated must then have a used overflow value of visible.
|
||||
// FIXME: Apply this to the used values, not the computed ones.
|
||||
overflow_origin_computed_values.set_overflow_x(CSS::Overflow::Visible);
|
||||
overflow_origin_computed_values.set_overflow_y(CSS::Overflow::Visible);
|
||||
}
|
||||
|
|
@ -5758,7 +5759,7 @@ GC::Ptr<Element const> Document::scrolling_element() const
|
|||
if (in_quirks_mode()) {
|
||||
// 1. If the body element exists, and it is not potentially scrollable, return the body element and abort these steps.
|
||||
// For this purpose, a value of overflow:clip on the the body element’s parent element must be treated as overflow:hidden.
|
||||
if (auto const* body_element = body(); body_element && !body_element->is_potentially_scrollable())
|
||||
if (auto const* body_element = body(); body_element && !body_element->is_potentially_scrollable(Element::TreatOverflowClipOnBodyParentAsOverflowHidden::Yes))
|
||||
return body_element;
|
||||
|
||||
// 2. Return null and abort these steps.
|
||||
|
|
|
|||
|
|
@ -1889,26 +1889,34 @@ void Element::set_tab_index(i32 tab_index)
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom-view/#potentially-scrollable
|
||||
bool Element::is_potentially_scrollable() const
|
||||
bool Element::is_potentially_scrollable(TreatOverflowClipOnBodyParentAsOverflowHidden treat_overflow_clip_on_body_parent_as_overflow_hidden = TreatOverflowClipOnBodyParentAsOverflowHidden::No) const
|
||||
{
|
||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||
const_cast<Document&>(document()).update_layout(UpdateLayoutReason::ElementIsPotentiallyScrollable);
|
||||
const_cast<Document&>(document()).update_style();
|
||||
|
||||
// NOTE: Since this should always be the body element, the body element must have a <html> element parent. See Document::body().
|
||||
VERIFY(parent());
|
||||
|
||||
// An element body (which will be the body element) is potentially scrollable if all of the following conditions are true:
|
||||
VERIFY(is<HTML::HTMLBodyElement>(this) || is<HTML::HTMLFrameSetElement>(this));
|
||||
|
||||
// Since this should always be the body element, the body element must have a <html> element parent. See Document::body().
|
||||
VERIFY(parent());
|
||||
|
||||
// - body has an associated box.
|
||||
if (!layout_node())
|
||||
return false;
|
||||
|
||||
// - body’s parent element’s computed value of the overflow-x or overflow-y properties is neither visible nor clip.
|
||||
if (parent()->layout_node()->computed_values().overflow_x() == CSS::Overflow::Visible || parent()->layout_node()->computed_values().overflow_y() == CSS::Overflow::Visible)
|
||||
return false;
|
||||
// NOTE: When treating 'overflow:clip' as 'overflow:hidden', we can never fail this condition
|
||||
if (treat_overflow_clip_on_body_parent_as_overflow_hidden == TreatOverflowClipOnBodyParentAsOverflowHidden::No && (parent()->layout_node()->computed_values().overflow_x() == CSS::Overflow::Clip || parent()->layout_node()->computed_values().overflow_y() == CSS::Overflow::Clip))
|
||||
return false;
|
||||
|
||||
// - body’s computed value of the overflow-x or overflow-y properties is neither visible nor clip.
|
||||
return layout_node()
|
||||
&& (parent()->layout_node()
|
||||
&& parent()->layout_node()->computed_values().overflow_x() != CSS::Overflow::Visible && parent()->layout_node()->computed_values().overflow_x() != CSS::Overflow::Clip
|
||||
&& parent()->layout_node()->computed_values().overflow_y() != CSS::Overflow::Visible && parent()->layout_node()->computed_values().overflow_y() != CSS::Overflow::Clip)
|
||||
&& (layout_node()->computed_values().overflow_x() != CSS::Overflow::Visible && layout_node()->computed_values().overflow_x() != CSS::Overflow::Clip
|
||||
&& layout_node()->computed_values().overflow_y() != CSS::Overflow::Visible && layout_node()->computed_values().overflow_y() != CSS::Overflow::Clip);
|
||||
if (first_is_one_of(layout_node()->computed_values().overflow_x(), CSS::Overflow::Visible, CSS::Overflow::Clip) || first_is_one_of(layout_node()->computed_values().overflow_y(), CSS::Overflow::Visible, CSS::Overflow::Clip))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
|
||||
|
|
|
|||
|
|
@ -335,7 +335,11 @@ public:
|
|||
i32 tab_index() const;
|
||||
void set_tab_index(i32 tab_index);
|
||||
|
||||
bool is_potentially_scrollable() const;
|
||||
enum class TreatOverflowClipOnBodyParentAsOverflowHidden {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
bool is_potentially_scrollable(TreatOverflowClipOnBodyParentAsOverflowHidden) const;
|
||||
|
||||
double scroll_top() const;
|
||||
double scroll_left() const;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 8 tests
|
||||
|
||||
4 Pass
|
||||
4 Fail
|
||||
Fail scrollingElement in quirks mode
|
||||
Pass scrollingElement in no-quirks mode
|
||||
Fail scrollingElement in quirks mode (body table)
|
||||
Pass scrollingElement in no-quirks mode (body table)
|
||||
Fail scrollingElement in quirks mode (root table)
|
||||
Pass scrollingElement in no-quirks mode (root table)
|
||||
Fail scrollingElement in quirks mode (root table, body table)
|
||||
Pass scrollingElement in no-quirks mode (root table, body table)
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>cssom-view - scrollingElement</title>
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
function makeDescription(rootDisplay, bodyDisplay) {
|
||||
let a = [];
|
||||
if (rootDisplay) {
|
||||
a.push(`root ${rootDisplay}`);
|
||||
}
|
||||
if (bodyDisplay) {
|
||||
a.push(`body ${bodyDisplay}`);
|
||||
}
|
||||
let s = a.join(", ");
|
||||
if (s) {
|
||||
s = ` (${s})`;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function quirksTest(rootDisplay, bodyDisplay) {
|
||||
async_test(function() {
|
||||
let quirksFrame = document.createElement("iframe");
|
||||
quirksFrame.onload = this.step_func_done(function() {
|
||||
var quirksDoc = quirksFrame.contentDocument;
|
||||
assert_equals(quirksDoc.compatMode, "BackCompat", "Should be in quirks mode.");
|
||||
assert_not_equals(quirksDoc.body, null, "Should have a body element");
|
||||
|
||||
quirksDoc.documentElement.style.display = rootDisplay;
|
||||
quirksDoc.body.style.display = bodyDisplay;
|
||||
|
||||
// Tests for quirks mode document.
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body,
|
||||
"scrollingElement in quirks mode should default to body element.");
|
||||
|
||||
quirksDoc.documentElement.style.overflow = "clip";
|
||||
quirksDoc.body.style.overflow = "auto";
|
||||
assert_equals(quirksDoc.scrollingElement, null);
|
||||
quirksDoc.body.style.overflow = "hidden";
|
||||
assert_equals(quirksDoc.scrollingElement, null);
|
||||
quirksDoc.body.style.overflow = "scroll";
|
||||
assert_equals(quirksDoc.scrollingElement, null);
|
||||
quirksDoc.body.style.overflow = "visible";
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
|
||||
quirksDoc.body.style.overflow = "clip";
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
|
||||
|
||||
quirksDoc.documentElement.style.overflow = "scroll";
|
||||
quirksDoc.body.style.overflow = "scroll";
|
||||
assert_equals(quirksDoc.scrollingElement, null,
|
||||
"scrollingElement in quirks mode should be null if overflow of body and root element isn't visible.");
|
||||
quirksDoc.documentElement.style.overflow = "visible";
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
|
||||
quirksDoc.documentElement.style.overflow = "scroll";
|
||||
quirksDoc.body.style.overflow = "visible";
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
|
||||
quirksDoc.documentElement.style.overflow = "visible";
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
|
||||
|
||||
quirksDoc.body.style.display = "none";
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body)
|
||||
quirksDoc.body.style.display = "block";
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
|
||||
|
||||
quirksDoc.documentElement.appendChild(quirksDoc.createElement("body"));
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.getElementsByTagName("body")[0]);
|
||||
quirksDoc.documentElement.removeChild(quirksDoc.documentElement.lastChild);
|
||||
assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
|
||||
|
||||
quirksDoc.documentElement.removeChild(quirksDoc.body);
|
||||
assert_equals(quirksDoc.scrollingElement, null);
|
||||
quirksDoc.documentElement.appendChild(quirksDoc.createElementNS("foobarNS", "body"));
|
||||
assert_equals(quirksDoc.scrollingElement, null);
|
||||
|
||||
quirksDoc.removeChild(quirksDoc.documentElement);
|
||||
assert_equals(quirksDoc.scrollingElement, null);
|
||||
|
||||
quirksDoc.appendChild(quirksDoc.createElementNS("foobarNS", "html"));
|
||||
quirksDoc.documentElement.appendChild(quirksDoc.createElement("body"));
|
||||
assert_equals(quirksDoc.scrollingElement, null);
|
||||
|
||||
quirksDoc.removeChild(quirksDoc.documentElement);
|
||||
quirksDoc.appendChild(quirksDoc.createElement("body"));
|
||||
assert_equals(quirksDoc.scrollingElement, null);
|
||||
|
||||
quirksFrame.remove();
|
||||
});
|
||||
quirksFrame.src =
|
||||
URL.createObjectURL(new Blob([], { type: "text/html" }));
|
||||
document.body.append(quirksFrame);
|
||||
}, `scrollingElement in quirks mode${makeDescription(rootDisplay, bodyDisplay)}`);
|
||||
}
|
||||
|
||||
function nonQuirksTest(rootDisplay, bodyDisplay) {
|
||||
async_test(function() {
|
||||
let nonQuirksFrame = document.createElement("iframe");
|
||||
nonQuirksFrame.onload = this.step_func_done(function() {
|
||||
var nonQuirksDoc = nonQuirksFrame.contentDocument;
|
||||
assert_equals(nonQuirksDoc.compatMode, "CSS1Compat", "Should be in standards mode.");
|
||||
assert_not_equals(nonQuirksDoc.body, null, "Should have a body element");
|
||||
|
||||
nonQuirksDoc.documentElement.style.display = rootDisplay;
|
||||
nonQuirksDoc.body.style.display = bodyDisplay;
|
||||
|
||||
assert_equals(nonQuirksDoc.scrollingElement, nonQuirksDoc.documentElement,
|
||||
"scrollingElement in standards mode should be the document element.");
|
||||
|
||||
for (let rootOverflow of ["", "clip", "scroll", "hidden", "visible"]) {
|
||||
for (let bodyOverflow of ["", "clip", "scroll", "hidden", "visible"]) {
|
||||
nonQuirksDoc.documentElement.style.overflow = rootOverflow;
|
||||
nonQuirksDoc.body.style.overflow = bodyOverflow;
|
||||
assert_equals(nonQuirksDoc.scrollingElement, nonQuirksDoc.documentElement);
|
||||
}
|
||||
}
|
||||
|
||||
nonQuirksDoc.removeChild(nonQuirksDoc.documentElement);
|
||||
assert_equals(nonQuirksDoc.scrollingElement, null);
|
||||
nonQuirksDoc.appendChild(nonQuirksDoc.createElement("foobar"));
|
||||
assert_equals(nonQuirksDoc.scrollingElement.localName, "foobar");
|
||||
|
||||
nonQuirksFrame.remove();
|
||||
});
|
||||
nonQuirksFrame.src =
|
||||
URL.createObjectURL(new Blob([`<!doctype html>`], { type: "text/html" }));
|
||||
document.body.append(nonQuirksFrame);
|
||||
}, `scrollingElement in no-quirks mode ${makeDescription(rootDisplay, bodyDisplay)}`);
|
||||
}
|
||||
|
||||
for (let rootDisplay of ["", "table"]) {
|
||||
for (let bodyDisplay of ["", "table"]) {
|
||||
quirksTest(rootDisplay, bodyDisplay);
|
||||
nonQuirksTest(rootDisplay, bodyDisplay);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Loading…
Reference in a new issue