offsetLeft and offsetParent should adjust across shadow boundaries
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 18 Dec 2018 03:52:53 +0000 (03:52 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 18 Dec 2018 03:52:53 +0000 (03:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=157437
<rdar://problem/26154021>

Reviewed by Simon Fraser.

Source/WebCore:

Update the WebKit's treatment of shadow boundaries in offsetLeft, offsetTop, and offsetParent to match
the latest discussion in CSS WG. See https://github.com/w3c/webcomponents/issues/497
and https://github.com/w3c/webcomponents/issues/763

The latest consensus is to use the retargeting algorithm (https://dom.spec.whatwg.org/#retarget).
In practice, this would mean that we need to keep walking up the offset parent ancestors until we find
the one which is in the same tree as a shadow-inclusive ancestor of the context object.

For example, if a node (the context object of offsetTop, offsetLeft, offsetParent) was assigned to a slot
inside a shadow tree and its offset parent was in the shadow tree, we need to walk up to its offset parent,
then its offset parent, etc... until we find the offset parent in the same tree as the context object.

Note it's possible that the context object is inside a shadow tree which does not have its own offset parent.
(e.g. all elements have position: static) For this reason, we need to consider not just offset parent in
the same tree as the context object but as well as any offset parent which is in its ancestor trees.

Test: fast/shadow-dom/offsetParent-across-shadow-boundaries.html

* dom/Element.cpp:
(WebCore::adjustOffsetForZoomAndSubpixelLayout): Extracted to share code between offsetLeft and offsetTop.
(WebCore::collectAncestorTreeScopeAsHashSet): Added.
(WebCore::Element::offsetLeftForBindings): Added. Sums up offsetLeft's until it finds the first offset parent
which is a shadow-including ancestor (https://dom.spec.whatwg.org/#concept-shadow-including-ancestor).
(WebCore::Element::offsetLeft): Now uses adjustOffsetForZoomAndSubpixelLayout.
(WebCore::Element::offsetTopForBindings): Added. Like offsetLeftForBindings, this function sums up offsetTop's
until it finds the first offset parent which is a shadow-including ancestor.
(WebCore::Element::offsetTop): Now uses adjustOffsetForZoomAndSubpixelLayout.
(WebCore::Element::offsetParentForBindings): Renamed from bindingsOffsetParent to be consistent with other
functions meant to be used for bindings code.
* dom/Element.h:
* html/HTMLElement.idl:

Source/WebKit:

Use *forBindings variants of offsetLeft, offsetTop, and offsetParent.

* WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMElementGtk.cpp:
(webkit_dom_element_get_offset_left):
(webkit_dom_element_get_offset_top):
(webkit_dom_element_get_offset_parent):

Source/WebKitLegacy/mac:

Use *forBindings variants of offsetLeft, offsetTop, and offsetParent.

* DOM/DOMElement.mm:
(-[DOMElement offsetLeft]):
(-[DOMElement offsetTop]):
(-[DOMElement offsetParent]):

LayoutTests:

Added a W3C style testharness.js test.

* fast/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt: Added.
* fast/shadow-dom/offsetParent-across-shadow-boundaries.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@239313 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt [new file with mode: 0644]
LayoutTests/fast/shadow-dom/offsetParent-across-shadow-boundaries.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/html/HTMLElement.idl
Source/WebKit/ChangeLog
Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMElementGtk.cpp
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/DOM/DOMElement.mm

index b9c64ba..9a0a1e8 100644 (file)
@@ -1,3 +1,16 @@
+2018-12-17  Ryosuke Niwa  <rniwa@webkit.org>
+
+        offsetLeft and offsetParent should adjust across shadow boundaries
+        https://bugs.webkit.org/show_bug.cgi?id=157437
+        <rdar://problem/26154021>
+
+        Reviewed by Simon Fraser.
+
+        Added a W3C style testharness.js test.
+
+        * fast/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt: Added.
+        * fast/shadow-dom/offsetParent-across-shadow-boundaries.html: Added.
+
 2018-12-17  Simon Fraser  <simon.fraser@apple.com>
 
         Don't use more expensive layer backing store formats when subpixel text antialiasing is not enabled
diff --git a/LayoutTests/fast/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt b/LayoutTests/fast/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt
new file mode 100644 (file)
index 0000000..bc2ce56
--- /dev/null
@@ -0,0 +1,18 @@
+
+PASS offsetParent must return the offset parent in the same shadow tree of open mode 
+PASS offsetParent must return the offset parent in the same shadow tree of closed mode 
+PASS offsetParent must return the offset parent in the same shadow tree of open mode even when nested 
+PASS offsetParent must return the offset parent in the same shadow tree of closed mode even when nested 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of open mode 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of closed mode 
+PASS offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of open mode did not have any offset parent 
+PASS offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of closed mode did not have any offset parent 
+PASS offsetParent must return null on a child element of a shadow host for the shadow tree in open mode which is not assigned to any slot 
+PASS offsetParent must return null on a child element of a shadow host for the shadow tree in closed mode which is not assigned to any slot 
+PASS offsetParent must return null on a child element of a shadow host for the shadow tree in open mode which is not in the flat tree 
+PASS offsetParent must return null on a child element of a shadow host for the shadow tree in closed mode which is not in the flat tree 
+
diff --git a/LayoutTests/fast/shadow-dom/offsetParent-across-shadow-boundaries.html b/LayoutTests/fast/shadow-dom/offsetParent-across-shadow-boundaries.html
new file mode 100644 (file)
index 0000000..c05147a
--- /dev/null
@@ -0,0 +1,190 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="offsetParent should only return nodes that are shadow including ancestor">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent">
+<link rel="help" href="https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/event-path-test-helpers.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<div id="container" style="position: relative"></div>
+<script>
+
+const container = document.getElementById('container');
+
+function testOffsetParentInShadowTree(mode) {
+    test(function () {
+        const host = document.createElement('div');
+        container.appendChild(host);
+        this.add_cleanup(() => host.remove());
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<div id="relativeParent" style="position: relative; padding-left: 100px; padding-top: 70px;"><div id="target"></div></div>';
+        const relativeParent = shadowRoot.getElementById('relativeParent');
+
+        assert_true(relativeParent instanceof HTMLDivElement);
+        const target = shadowRoot.getElementById('target');
+        assert_equals(target.offsetParent, relativeParent);
+        assert_equals(target.offsetLeft, 100);
+        assert_equals(target.offsetTop, 70);
+    }, `offsetParent must return the offset parent in the same shadow tree of ${mode} mode`);
+}
+
+testOffsetParentInShadowTree('open');
+testOffsetParentInShadowTree('closed');
+
+function testOffsetParentInNestedShadowTrees(mode) {
+    test(function () {
+        const outerHost = document.createElement('section');
+        container.appendChild(outerHost);
+        this.add_cleanup(() => outerHost.remove());
+        const outerShadow = outerHost.attachShadow({mode});
+        outerShadow.innerHTML = '<section id="outerParent" style="position: absolute; top: 50px; left: 50px;"></section>';
+
+        const innerHost = document.createElement('div');
+        outerShadow.firstChild.appendChild(innerHost);
+        const innerShadow = innerHost.attachShadow({mode});
+        innerShadow.innerHTML = '<div id="innerParent" style="position: relative; padding-left: 60px; padding-top: 40px;"><div id="target"></div></div>';
+        const innerParent = innerShadow.getElementById('innerParent');
+
+        const target = innerShadow.getElementById('target');
+        assert_true(innerParent instanceof HTMLDivElement);
+        assert_equals(target.offsetParent, innerParent);
+        assert_equals(target.offsetLeft, 60);
+        assert_equals(target.offsetTop, 40);
+
+        outerHost.remove();
+    }, `offsetParent must return the offset parent in the same shadow tree of ${mode} mode even when nested`);
+}
+
+testOffsetParentInNestedShadowTrees('open');
+testOffsetParentInNestedShadowTrees('closed');
+
+function testOffsetParentOnElementAssignedToSlotInsideOffsetParent(mode) {
+    test(function () {
+        const host = document.createElement('div');
+        host.innerHTML = '<div id="target"></div>'
+        container.appendChild(host);
+        this.add_cleanup(() => host.remove());
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<div style="position: relative; padding-left: 85px; padding-top: 45px;"><slot></slot></div>';
+        const target = host.querySelector('#target');
+        assert_equals(target.offsetParent, container);
+        assert_equals(target.offsetLeft, 85);
+        assert_equals(target.offsetTop, 45);
+    }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
+}
+
+testOffsetParentOnElementAssignedToSlotInsideOffsetParent('open');
+testOffsetParentOnElementAssignedToSlotInsideOffsetParent('closed');
+
+function testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents(mode) {
+    test(function () {
+        const host = document.createElement('div');
+        host.innerHTML = '<div id="target" style="border:solid 1px blue;">hi</div>';
+        const previousBlock = document.createElement('div');
+        previousBlock.style.height = '12px';
+        container.append(previousBlock, host);
+        this.add_cleanup(() => container.innerHTML = '');
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<section style="position: relative; margin-left: 20px; margin-top: 100px; background: #ccc"><div style="position: absolute; top: 10px; left: 10px;"><slot></slot></div></section>';
+        const target = host.querySelector('#target');
+        assert_equals(target.offsetParent, container);
+        assert_equals(target.offsetLeft, 30);
+        assert_equals(target.offsetTop, 122);
+    }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
+}
+
+testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents('open');
+testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents('closed');
+
+function testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees(mode) {
+    test(function () {
+        const outerHost = document.createElement('section');
+        outerHost.innerHTML = '<div id="target"></div>';
+        container.appendChild(outerHost);
+        this.add_cleanup(() => outerHost.remove());
+        const outerShadow = outerHost.attachShadow({mode});
+        outerShadow.innerHTML = '<section style="position: absolute; top: 40px; left: 50px;"><div id="innerHost"><slot></slot></div></section>';
+
+        const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
+        innerShadow.innerHTML = '<div style="position: absolute; top: 200px; margin-left: 100px;"><slot></slot></div>';
+
+        const target = outerHost.querySelector('#target');
+        assert_equals(target.offsetParent, container);
+        assert_equals(target.offsetLeft, 150);
+        assert_equals(target.offsetTop, 240);
+        outerHost.remove();
+    }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of ${mode} mode`);
+}
+
+testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees('open');
+testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees('closed');
+
+function testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent(mode) {
+    test(function () {
+        const outerHost = document.createElement('section');
+        container.appendChild(outerHost);
+        this.add_cleanup(() => outerHost.remove());
+        const outerShadow = outerHost.attachShadow({mode});
+        outerShadow.innerHTML = '<div id="innerHost"><div id="target"></div></div>';
+
+        const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
+        innerShadow.innerHTML = '<div style="position: absolute; top: 23px; left: 24px;"><slot></slot></div>';
+
+        const target = outerShadow.querySelector('#target');
+        assert_equals(target.offsetParent, container);
+        assert_equals(target.offsetLeft, 24);
+        assert_equals(target.offsetTop, 23);
+    }, `offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of ${mode} mode did not have any offset parent`);
+}
+
+testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent('open');
+testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent('closed');
+
+function testOffsetParentOnUnassignedChild(mode) {
+    test(function () {
+        const host = document.createElement('section');
+        host.innerHTML = '<div id="target"></div>';
+        this.add_cleanup(() => host.remove());
+        container.appendChild(host);
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<section style="position: absolute; top: 50px; left: 50px;">content</section>';
+        const target = host.querySelector('#target');
+        assert_equals(target.offsetParent, null);
+        assert_equals(target.offsetLeft, 0);
+        assert_equals(target.offsetTop, 0);
+    }, `offsetParent must return null on a child element of a shadow host for the shadow tree in ${mode} mode which is not assigned to any slot`);
+}
+
+testOffsetParentOnUnassignedChild('open');
+testOffsetParentOnUnassignedChild('closed');
+
+function testOffsetParentOnAssignedChildNotInFlatTree(mode) {
+    test(function () {
+        const outerHost = document.createElement('section');
+        outerHost.innerHTML = '<div id="target"></div>';
+        container.appendChild(outerHost);
+        this.add_cleanup(() => outerHost.remove());
+        const outerShadow = outerHost.attachShadow({mode});
+        outerShadow.innerHTML = '<div id="innerHost"><div style="position: absolute; top: 50px; left: 50px;"><slot></slot></div></div>';
+
+        const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
+        innerShadow.innerHTML = '<div>content</div>';
+
+        const target = outerHost.querySelector('#target');
+        assert_equals(target.offsetParent, null);
+        assert_equals(target.offsetLeft, 0);
+        assert_equals(target.offsetTop, 0);
+    }, `offsetParent must return null on a child element of a shadow host for the shadow tree in ${mode} mode which is not in the flat tree`);
+}
+
+testOffsetParentOnAssignedChildNotInFlatTree('open');
+testOffsetParentOnAssignedChildNotInFlatTree('closed');
+
+</script>
+</body>
+</html>
index 8617b02..9134293 100644 (file)
@@ -1,3 +1,43 @@
+2018-12-17  Ryosuke Niwa  <rniwa@webkit.org>
+
+        offsetLeft and offsetParent should adjust across shadow boundaries
+        https://bugs.webkit.org/show_bug.cgi?id=157437
+        <rdar://problem/26154021>
+
+        Reviewed by Simon Fraser.
+
+        Update the WebKit's treatment of shadow boundaries in offsetLeft, offsetTop, and offsetParent to match
+        the latest discussion in CSS WG. See https://github.com/w3c/webcomponents/issues/497
+        and https://github.com/w3c/webcomponents/issues/763
+
+        The latest consensus is to use the retargeting algorithm (https://dom.spec.whatwg.org/#retarget).
+        In practice, this would mean that we need to keep walking up the offset parent ancestors until we find
+        the one which is in the same tree as a shadow-inclusive ancestor of the context object.
+
+        For example, if a node (the context object of offsetTop, offsetLeft, offsetParent) was assigned to a slot
+        inside a shadow tree and its offset parent was in the shadow tree, we need to walk up to its offset parent,
+        then its offset parent, etc... until we find the offset parent in the same tree as the context object.
+
+        Note it's possible that the context object is inside a shadow tree which does not have its own offset parent.
+        (e.g. all elements have position: static) For this reason, we need to consider not just offset parent in
+        the same tree as the context object but as well as any offset parent which is in its ancestor trees.
+
+        Test: fast/shadow-dom/offsetParent-across-shadow-boundaries.html
+
+        * dom/Element.cpp:
+        (WebCore::adjustOffsetForZoomAndSubpixelLayout): Extracted to share code between offsetLeft and offsetTop.
+        (WebCore::collectAncestorTreeScopeAsHashSet): Added.
+        (WebCore::Element::offsetLeftForBindings): Added. Sums up offsetLeft's until it finds the first offset parent
+        which is a shadow-including ancestor (https://dom.spec.whatwg.org/#concept-shadow-including-ancestor).
+        (WebCore::Element::offsetLeft): Now uses adjustOffsetForZoomAndSubpixelLayout.
+        (WebCore::Element::offsetTopForBindings): Added. Like offsetLeftForBindings, this function sums up offsetTop's
+        until it finds the first offset parent which is a shadow-including ancestor.
+        (WebCore::Element::offsetTop): Now uses adjustOffsetForZoomAndSubpixelLayout.
+        (WebCore::Element::offsetParentForBindings): Renamed from bindingsOffsetParent to be consistent with other
+        functions meant to be used for bindings code.
+        * dom/Element.h:
+        * html/HTMLElement.idl:
+
 2018-12-17  Simon Fraser  <simon.fraser@apple.com>
 
         Don't use more expensive layer backing store formats when subpixel text antialiasing is not enabled
index 6468eab..32a3935 100644 (file)
@@ -883,27 +883,77 @@ static double convertToNonSubpixelValueIfNeeded(double value, const Document& do
     return subpixelMetricsEnabled(document) ? value : roundStrategy == Round ? round(value) : floor(value);
 }
 
+static double adjustOffsetForZoomAndSubpixelLayout(RenderBoxModelObject* renderer, const LayoutUnit& offset)
+{
+    LayoutUnit offsetLeft = subpixelMetricsEnabled(renderer->document()) ? offset : LayoutUnit(roundToInt(offset));
+    double zoomFactor = 1;
+    double offsetLeftAdjustedWithZoom = adjustForLocalZoom(offsetLeft, *renderer, zoomFactor);
+    return convertToNonSubpixelValueIfNeeded(offsetLeftAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round);
+}
+
+static HashSet<TreeScope*> collectAncestorTreeScopeAsHashSet(Node& node)
+{
+    HashSet<TreeScope*> ancestors;
+    for (auto* currentScope = &node.treeScope(); currentScope; currentScope = currentScope->parentTreeScope())
+        ancestors.add(currentScope);
+    return ancestors;
+}
+
+double Element::offsetLeftForBindings()
+{
+    auto offset = offsetLeft();
+
+    auto parent = makeRefPtr(offsetParent());
+    if (!parent || !parent->isInShadowTree())
+        return offset;
+
+    ASSERT(&parent->document() == &document());
+    if (&parent->treeScope() == &treeScope())
+        return offset;
+
+    auto ancestorTreeScopes = collectAncestorTreeScopeAsHashSet(*this);
+    while (parent && !ancestorTreeScopes.contains(&parent->treeScope())) {
+        offset += parent->offsetLeft();
+        parent = parent->offsetParent();
+    }
+
+    return offset;
+}
+
 double Element::offsetLeft()
 {
     document().updateLayoutIgnorePendingStylesheets();
-    if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
-        LayoutUnit offsetLeft = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetLeft() : LayoutUnit(roundToInt(renderer->offsetLeft()));
-        double zoomFactor = 1;
-        double offsetLeftAdjustedWithZoom = adjustForLocalZoom(offsetLeft, *renderer, zoomFactor);
-        return convertToNonSubpixelValueIfNeeded(offsetLeftAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round);
-    }
+    if (RenderBoxModelObject* renderer = renderBoxModelObject())
+        return adjustOffsetForZoomAndSubpixelLayout(renderer, renderer->offsetLeft());
     return 0;
 }
 
+double Element::offsetTopForBindings()
+{
+    auto offset = offsetTop();
+
+    auto parent = makeRefPtr(offsetParent());
+    if (!parent || !parent->isInShadowTree())
+        return offset;
+
+    ASSERT(&parent->document() == &document());
+    if (&parent->treeScope() == &treeScope())
+        return offset;
+
+    auto ancestorTreeScopes = collectAncestorTreeScopeAsHashSet(*this);
+    while (parent && !ancestorTreeScopes.contains(&parent->treeScope())) {
+        offset += parent->offsetTop();
+        parent = parent->offsetParent();
+    }
+
+    return offset;
+}
+
 double Element::offsetTop()
 {
     document().updateLayoutIgnorePendingStylesheets();
-    if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
-        LayoutUnit offsetTop = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetTop() : LayoutUnit(roundToInt(renderer->offsetTop()));
-        double zoomFactor = 1;
-        double offsetTopAdjustedWithZoom = adjustForLocalZoom(offsetTop, *renderer, zoomFactor);
-        return convertToNonSubpixelValueIfNeeded(offsetTopAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round);
-    }
+    if (RenderBoxModelObject* renderer = renderBoxModelObject())
+        return adjustOffsetForZoomAndSubpixelLayout(renderer, renderer->offsetTop());
     return 0;
 }
 
@@ -927,12 +977,14 @@ double Element::offsetHeight()
     return 0;
 }
 
-Element* Element::bindingsOffsetParent()
+Element* Element::offsetParentForBindings()
 {
     Element* element = offsetParent();
     if (!element || !element->isInShadowTree())
         return element;
-    return element->containingShadowRoot()->mode() == ShadowRootMode::UserAgent ? nullptr : element;
+    while (element && !isDescendantOrShadowDescendantOf(&element->rootNode()))
+        element = element->offsetParent();
+    return element;
 }
 
 Element* Element::offsetParent()
index bc6eba8..731e076 100644 (file)
@@ -156,7 +156,9 @@ public:
     WEBCORE_EXPORT void scrollByLines(int lines);
     WEBCORE_EXPORT void scrollByPages(int pages);
 
+    WEBCORE_EXPORT double offsetLeftForBindings();
     WEBCORE_EXPORT double offsetLeft();
+    WEBCORE_EXPORT double offsetTopForBindings();
     WEBCORE_EXPORT double offsetTop();
     WEBCORE_EXPORT double offsetWidth();
     WEBCORE_EXPORT double offsetHeight();
@@ -165,7 +167,7 @@ public:
 
     // FIXME: Replace uses of offsetParent in the platform with calls
     // to the render layer and merge bindingsOffsetParent and offsetParent.
-    WEBCORE_EXPORT Element* bindingsOffsetParent();
+    WEBCORE_EXPORT Element* offsetParentForBindings();
 
     const Element* rootElement() const;
 
index 90dd63a..798be79 100644 (file)
@@ -52,9 +52,9 @@
     readonly attribute boolean isContentEditable;
 
     // Extensions from CSSOM-view specification (https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface).
-    [ImplementedAs=bindingsOffsetParent] readonly attribute Element? offsetParent;
-    readonly attribute double offsetTop; // FIXME: Should be of type long.
-    readonly attribute double offsetLeft; // FIXME: Should be of type long.
+    [ImplementedAs=offsetParentForBindings] readonly attribute Element? offsetParent;
+    [ImplementedAs=offsetTopForBindings] readonly attribute double offsetTop; // FIXME: Should be of type long.
+    [ImplementedAs=offsetLeftForBindings] readonly attribute double offsetLeft; // FIXME: Should be of type long.
     readonly attribute double offsetWidth; // FIXME: Should be of type long.
     readonly attribute double offsetHeight; // FIXME: Should be of type long.
 
index 2deddaa..5e1cf3b 100644 (file)
@@ -1,3 +1,18 @@
+2018-12-17  Ryosuke Niwa  <rniwa@webkit.org>
+
+        offsetLeft and offsetParent should adjust across shadow boundaries
+        https://bugs.webkit.org/show_bug.cgi?id=157437
+        <rdar://problem/26154021>
+
+        Reviewed by Simon Fraser.
+
+        Use *forBindings variants of offsetLeft, offsetTop, and offsetParent.
+
+        * WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMElementGtk.cpp:
+        (webkit_dom_element_get_offset_left):
+        (webkit_dom_element_get_offset_top):
+        (webkit_dom_element_get_offset_parent):
+
 2018-12-17  Chris Fleizach  <cfleizach@apple.com>
 
         Some builds are broken after r239262
index a26cc72..cd778d0 100644 (file)
@@ -1088,7 +1088,7 @@ gdouble webkit_dom_element_get_offset_left(WebKitDOMElement* self)
     WebCore::JSMainThreadNullState state;
     g_return_val_if_fail(WEBKIT_DOM_IS_ELEMENT(self), 0);
     WebCore::Element* item = WebKit::core(self);
-    gdouble result = item->offsetLeft();
+    gdouble result = item->offsetLeftForBindings();
     return result;
 }
 
@@ -1097,7 +1097,7 @@ gdouble webkit_dom_element_get_offset_top(WebKitDOMElement* self)
     WebCore::JSMainThreadNullState state;
     g_return_val_if_fail(WEBKIT_DOM_IS_ELEMENT(self), 0);
     WebCore::Element* item = WebKit::core(self);
-    gdouble result = item->offsetTop();
+    gdouble result = item->offsetTopForBindings();
     return result;
 }
 
@@ -1228,7 +1228,7 @@ WebKitDOMElement* webkit_dom_element_get_offset_parent(WebKitDOMElement* self)
     WebCore::JSMainThreadNullState state;
     g_return_val_if_fail(WEBKIT_DOM_IS_ELEMENT(self), 0);
     WebCore::Element* item = WebKit::core(self);
-    RefPtr<WebCore::Element> gobjectResult = WTF::getPtr(item->bindingsOffsetParent());
+    RefPtr<WebCore::Element> gobjectResult = WTF::getPtr(item->offsetParentForBindings());
     return WebKit::kit(gobjectResult.get());
 }
 
index 48ad40d..e24389c 100644 (file)
@@ -1,3 +1,18 @@
+2018-12-17  Ryosuke Niwa  <rniwa@webkit.org>
+
+        offsetLeft and offsetParent should adjust across shadow boundaries
+        https://bugs.webkit.org/show_bug.cgi?id=157437
+        <rdar://problem/26154021>
+
+        Reviewed by Simon Fraser.
+
+        Use *forBindings variants of offsetLeft, offsetTop, and offsetParent.
+
+        * DOM/DOMElement.mm:
+        (-[DOMElement offsetLeft]):
+        (-[DOMElement offsetTop]):
+        (-[DOMElement offsetParent]):
+
 2018-12-17  Zalan Bujtas  <zalan@apple.com>
 
         Unreviewed build fix.
index 483a336..0b6432d 100644 (file)
@@ -81,13 +81,13 @@ DOMElement *kit(WebCore::Element* value)
 - (int)offsetLeft
 {
     WebCore::JSMainThreadNullState state;
-    return unwrap(*self).offsetLeft();
+    return unwrap(*self).offsetLeftForBindings();
 }
 
 - (int)offsetTop
 {
     WebCore::JSMainThreadNullState state;
-    return unwrap(*self).offsetTop();
+    return unwrap(*self).offsetTopForBindings();
 }
 
 - (int)offsetWidth
@@ -165,7 +165,7 @@ DOMElement *kit(WebCore::Element* value)
 - (DOMElement *)offsetParent
 {
     WebCore::JSMainThreadNullState state;
-    return kit(unwrap(*self).bindingsOffsetParent());
+    return kit(unwrap(*self).offsetParentForBindings());
 }
 
 - (NSString *)innerHTML