Changeset 239313 in webkit


Ignore:
Timestamp:
Dec 17, 2018 7:52:53 PM (5 years ago)
Author:
rniwa@webkit.org
Message:

offsetLeft and offsetParent should adjust across shadow boundaries
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.
Location:
trunk
Files:
2 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r239306 r239313  
     12018-12-17  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        offsetLeft and offsetParent should adjust across shadow boundaries
     4        https://bugs.webkit.org/show_bug.cgi?id=157437
     5        <rdar://problem/26154021>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Added a W3C style testharness.js test.
     10
     11        * fast/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt: Added.
     12        * fast/shadow-dom/offsetParent-across-shadow-boundaries.html: Added.
     13
    1142018-12-17  Simon Fraser  <simon.fraser@apple.com>
    215
  • trunk/Source/WebCore/ChangeLog

    r239306 r239313  
     12018-12-17  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        offsetLeft and offsetParent should adjust across shadow boundaries
     4        https://bugs.webkit.org/show_bug.cgi?id=157437
     5        <rdar://problem/26154021>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Update the WebKit's treatment of shadow boundaries in offsetLeft, offsetTop, and offsetParent to match
     10        the latest discussion in CSS WG. See https://github.com/w3c/webcomponents/issues/497
     11        and https://github.com/w3c/webcomponents/issues/763
     12
     13        The latest consensus is to use the retargeting algorithm (https://dom.spec.whatwg.org/#retarget).
     14        In practice, this would mean that we need to keep walking up the offset parent ancestors until we find
     15        the one which is in the same tree as a shadow-inclusive ancestor of the context object.
     16
     17        For example, if a node (the context object of offsetTop, offsetLeft, offsetParent) was assigned to a slot
     18        inside a shadow tree and its offset parent was in the shadow tree, we need to walk up to its offset parent,
     19        then its offset parent, etc... until we find the offset parent in the same tree as the context object.
     20
     21        Note it's possible that the context object is inside a shadow tree which does not have its own offset parent.
     22        (e.g. all elements have position: static) For this reason, we need to consider not just offset parent in
     23        the same tree as the context object but as well as any offset parent which is in its ancestor trees.
     24
     25        Test: fast/shadow-dom/offsetParent-across-shadow-boundaries.html
     26
     27        * dom/Element.cpp:
     28        (WebCore::adjustOffsetForZoomAndSubpixelLayout): Extracted to share code between offsetLeft and offsetTop.
     29        (WebCore::collectAncestorTreeScopeAsHashSet): Added.
     30        (WebCore::Element::offsetLeftForBindings): Added. Sums up offsetLeft's until it finds the first offset parent
     31        which is a shadow-including ancestor (https://dom.spec.whatwg.org/#concept-shadow-including-ancestor).
     32        (WebCore::Element::offsetLeft): Now uses adjustOffsetForZoomAndSubpixelLayout.
     33        (WebCore::Element::offsetTopForBindings): Added. Like offsetLeftForBindings, this function sums up offsetTop's
     34        until it finds the first offset parent which is a shadow-including ancestor.
     35        (WebCore::Element::offsetTop): Now uses adjustOffsetForZoomAndSubpixelLayout.
     36        (WebCore::Element::offsetParentForBindings): Renamed from bindingsOffsetParent to be consistent with other
     37        functions meant to be used for bindings code.
     38        * dom/Element.h:
     39        * html/HTMLElement.idl:
     40
    1412018-12-17  Simon Fraser  <simon.fraser@apple.com>
    242
  • trunk/Source/WebCore/dom/Element.cpp

    r239160 r239313  
    884884}
    885885
     886static double adjustOffsetForZoomAndSubpixelLayout(RenderBoxModelObject* renderer, const LayoutUnit& offset)
     887{
     888    LayoutUnit offsetLeft = subpixelMetricsEnabled(renderer->document()) ? offset : LayoutUnit(roundToInt(offset));
     889    double zoomFactor = 1;
     890    double offsetLeftAdjustedWithZoom = adjustForLocalZoom(offsetLeft, *renderer, zoomFactor);
     891    return convertToNonSubpixelValueIfNeeded(offsetLeftAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round);
     892}
     893
     894static HashSet<TreeScope*> collectAncestorTreeScopeAsHashSet(Node& node)
     895{
     896    HashSet<TreeScope*> ancestors;
     897    for (auto* currentScope = &node.treeScope(); currentScope; currentScope = currentScope->parentTreeScope())
     898        ancestors.add(currentScope);
     899    return ancestors;
     900}
     901
     902double Element::offsetLeftForBindings()
     903{
     904    auto offset = offsetLeft();
     905
     906    auto parent = makeRefPtr(offsetParent());
     907    if (!parent || !parent->isInShadowTree())
     908        return offset;
     909
     910    ASSERT(&parent->document() == &document());
     911    if (&parent->treeScope() == &treeScope())
     912        return offset;
     913
     914    auto ancestorTreeScopes = collectAncestorTreeScopeAsHashSet(*this);
     915    while (parent && !ancestorTreeScopes.contains(&parent->treeScope())) {
     916        offset += parent->offsetLeft();
     917        parent = parent->offsetParent();
     918    }
     919
     920    return offset;
     921}
     922
    886923double Element::offsetLeft()
    887924{
    888925    document().updateLayoutIgnorePendingStylesheets();
    889     if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
    890         LayoutUnit offsetLeft = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetLeft() : LayoutUnit(roundToInt(renderer->offsetLeft()));
    891         double zoomFactor = 1;
    892         double offsetLeftAdjustedWithZoom = adjustForLocalZoom(offsetLeft, *renderer, zoomFactor);
    893         return convertToNonSubpixelValueIfNeeded(offsetLeftAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round);
    894     }
     926    if (RenderBoxModelObject* renderer = renderBoxModelObject())
     927        return adjustOffsetForZoomAndSubpixelLayout(renderer, renderer->offsetLeft());
    895928    return 0;
    896929}
    897930
     931double Element::offsetTopForBindings()
     932{
     933    auto offset = offsetTop();
     934
     935    auto parent = makeRefPtr(offsetParent());
     936    if (!parent || !parent->isInShadowTree())
     937        return offset;
     938
     939    ASSERT(&parent->document() == &document());
     940    if (&parent->treeScope() == &treeScope())
     941        return offset;
     942
     943    auto ancestorTreeScopes = collectAncestorTreeScopeAsHashSet(*this);
     944    while (parent && !ancestorTreeScopes.contains(&parent->treeScope())) {
     945        offset += parent->offsetTop();
     946        parent = parent->offsetParent();
     947    }
     948
     949    return offset;
     950}
     951
    898952double Element::offsetTop()
    899953{
    900954    document().updateLayoutIgnorePendingStylesheets();
    901     if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
    902         LayoutUnit offsetTop = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetTop() : LayoutUnit(roundToInt(renderer->offsetTop()));
    903         double zoomFactor = 1;
    904         double offsetTopAdjustedWithZoom = adjustForLocalZoom(offsetTop, *renderer, zoomFactor);
    905         return convertToNonSubpixelValueIfNeeded(offsetTopAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round);
    906     }
     955    if (RenderBoxModelObject* renderer = renderBoxModelObject())
     956        return adjustOffsetForZoomAndSubpixelLayout(renderer, renderer->offsetTop());
    907957    return 0;
    908958}
     
    928978}
    929979
    930 Element* Element::bindingsOffsetParent()
     980Element* Element::offsetParentForBindings()
    931981{
    932982    Element* element = offsetParent();
    933983    if (!element || !element->isInShadowTree())
    934984        return element;
    935     return element->containingShadowRoot()->mode() == ShadowRootMode::UserAgent ? nullptr : element;
     985    while (element && !isDescendantOrShadowDescendantOf(&element->rootNode()))
     986        element = element->offsetParent();
     987    return element;
    936988}
    937989
  • trunk/Source/WebCore/dom/Element.h

    r238097 r239313  
    157157    WEBCORE_EXPORT void scrollByPages(int pages);
    158158
     159    WEBCORE_EXPORT double offsetLeftForBindings();
    159160    WEBCORE_EXPORT double offsetLeft();
     161    WEBCORE_EXPORT double offsetTopForBindings();
    160162    WEBCORE_EXPORT double offsetTop();
    161163    WEBCORE_EXPORT double offsetWidth();
     
    166168    // FIXME: Replace uses of offsetParent in the platform with calls
    167169    // to the render layer and merge bindingsOffsetParent and offsetParent.
    168     WEBCORE_EXPORT Element* bindingsOffsetParent();
     170    WEBCORE_EXPORT Element* offsetParentForBindings();
    169171
    170172    const Element* rootElement() const;
  • trunk/Source/WebCore/html/HTMLElement.idl

    r236439 r239313  
    5353
    5454    // Extensions from CSSOM-view specification (https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface).
    55     [ImplementedAs=bindingsOffsetParent] readonly attribute Element? offsetParent;
    56     readonly attribute double offsetTop; // FIXME: Should be of type long.
    57     readonly attribute double offsetLeft; // FIXME: Should be of type long.
     55    [ImplementedAs=offsetParentForBindings] readonly attribute Element? offsetParent;
     56    [ImplementedAs=offsetTopForBindings] readonly attribute double offsetTop; // FIXME: Should be of type long.
     57    [ImplementedAs=offsetLeftForBindings] readonly attribute double offsetLeft; // FIXME: Should be of type long.
    5858    readonly attribute double offsetWidth; // FIXME: Should be of type long.
    5959    readonly attribute double offsetHeight; // FIXME: Should be of type long.
  • trunk/Source/WebKit/ChangeLog

    r239311 r239313  
     12018-12-17  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        offsetLeft and offsetParent should adjust across shadow boundaries
     4        https://bugs.webkit.org/show_bug.cgi?id=157437
     5        <rdar://problem/26154021>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Use *forBindings variants of offsetLeft, offsetTop, and offsetParent.
     10
     11        * WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMElementGtk.cpp:
     12        (webkit_dom_element_get_offset_left):
     13        (webkit_dom_element_get_offset_top):
     14        (webkit_dom_element_get_offset_parent):
     15
    1162018-12-17  Chris Fleizach  <cfleizach@apple.com>
    217
  • trunk/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMElementGtk.cpp

    r235659 r239313  
    10891089    g_return_val_if_fail(WEBKIT_DOM_IS_ELEMENT(self), 0);
    10901090    WebCore::Element* item = WebKit::core(self);
    1091     gdouble result = item->offsetLeft();
     1091    gdouble result = item->offsetLeftForBindings();
    10921092    return result;
    10931093}
     
    10981098    g_return_val_if_fail(WEBKIT_DOM_IS_ELEMENT(self), 0);
    10991099    WebCore::Element* item = WebKit::core(self);
    1100     gdouble result = item->offsetTop();
     1100    gdouble result = item->offsetTopForBindings();
    11011101    return result;
    11021102}
     
    12291229    g_return_val_if_fail(WEBKIT_DOM_IS_ELEMENT(self), 0);
    12301230    WebCore::Element* item = WebKit::core(self);
    1231     RefPtr<WebCore::Element> gobjectResult = WTF::getPtr(item->bindingsOffsetParent());
     1231    RefPtr<WebCore::Element> gobjectResult = WTF::getPtr(item->offsetParentForBindings());
    12321232    return WebKit::kit(gobjectResult.get());
    12331233}
  • trunk/Source/WebKitLegacy/mac/ChangeLog

    r239280 r239313  
     12018-12-17  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        offsetLeft and offsetParent should adjust across shadow boundaries
     4        https://bugs.webkit.org/show_bug.cgi?id=157437
     5        <rdar://problem/26154021>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Use *forBindings variants of offsetLeft, offsetTop, and offsetParent.
     10
     11        * DOM/DOMElement.mm:
     12        (-[DOMElement offsetLeft]):
     13        (-[DOMElement offsetTop]):
     14        (-[DOMElement offsetParent]):
     15
    1162018-12-17  Zalan Bujtas  <zalan@apple.com>
    217
  • trunk/Source/WebKitLegacy/mac/DOM/DOMElement.mm

    r237266 r239313  
    8282{
    8383    WebCore::JSMainThreadNullState state;
    84     return unwrap(*self).offsetLeft();
     84    return unwrap(*self).offsetLeftForBindings();
    8585}
    8686
     
    8888{
    8989    WebCore::JSMainThreadNullState state;
    90     return unwrap(*self).offsetTop();
     90    return unwrap(*self).offsetTopForBindings();
    9191}
    9292
     
    166166{
    167167    WebCore::JSMainThreadNullState state;
    168     return kit(unwrap(*self).bindingsOffsetParent());
     168    return kit(unwrap(*self).offsetParentForBindings());
    169169}
    170170
Note: See TracChangeset for help on using the changeset viewer.