From be69af848852c554b23cf8c517028c445bd1509c Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Thu, 3 Oct 2019 20:34:54 +0000 Subject: [PATCH] Bug 1514074 - Retarget results of offset* DOM APIs. r=smaug Per https://github.com/w3c/csswg-drafts/issues/159. The test is imported from WebKit, who has already implemented this, with a few fixes to avoid duplicate test names and non-undefined return values from add_cleanup. See the diff attached to the bug. Differential Revision: https://phabricator.services.mozilla.com/D15938 UltraBlame original commit: e0a5cb126f2f084e5f7f3d5f0be0222a7fd67479 --- dom/html/nsGenericHTMLElement.cpp | 54 ++++- ...offsetParent-across-shadow-boundaries.html | 190 ++++++++++++++++++ 2 files changed, 236 insertions(+), 8 deletions(-) create mode 100644 testing/web-platform/tests/shadow-dom/offsetParent-across-shadow-boundaries.html diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index b50f76b702655..f493152e5d239 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -203,12 +203,15 @@ static bool IsOffsetParent(nsIFrame* aFrame) { return false; } -Element* nsGenericHTMLElement::GetOffsetRect(CSSIntRect& aRect) { - aRect = CSSIntRect(); +struct OffsetResult { + Element* mParent = nullptr; + CSSIntRect mRect; +}; - nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); +static OffsetResult GetUnretargetedOffsetsFor(const Element& aElement) { + nsIFrame* frame = aElement.GetPrimaryFrame(); if (!frame) { - return nullptr; + return {}; } nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(frame); @@ -217,7 +220,7 @@ Element* nsGenericHTMLElement::GetOffsetRect(CSSIntRect& aRect) { nsPoint origin(0, 0); nsIContent* offsetParent = nullptr; - Element* docElement = GetComposedDoc()->GetRootElement(); + Element* docElement = aElement.GetComposedDoc()->GetRootElement(); nsIContent* content = frame->GetContent(); if (content && @@ -270,7 +273,7 @@ Element* nsGenericHTMLElement::GetOffsetRect(CSSIntRect& aRect) { - offsetParent = GetComposedDoc()->GetBodyElement(); + offsetParent = aElement.GetComposedDoc()->GetBodyElement(); } } @@ -289,9 +292,44 @@ Element* nsGenericHTMLElement::GetOffsetRect(CSSIntRect& aRect) { nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame); rcFrame.MoveTo(origin); - aRect = CSSIntRect::FromAppUnitsRounded(rcFrame); + return {Element::FromNodeOrNull(offsetParent), + CSSIntRect::FromAppUnitsRounded(rcFrame)}; +} + +static bool ShouldBeRetargeted(const Element& aReferenceElement, + const Element& aElementToMaybeRetarget) { + ShadowRoot* shadow = aElementToMaybeRetarget.GetContainingShadow(); + if (!shadow) { + return false; + } + for (ShadowRoot* scope = aReferenceElement.GetContainingShadow(); scope; + scope = scope->Host()->GetContainingShadow()) { + if (scope == shadow) { + return false; + } + } + + return true; +} + +Element* nsGenericHTMLElement::GetOffsetRect(CSSIntRect& aRect) { + aRect = CSSIntRect(); + + if (!GetPrimaryFrame(FlushType::Layout)) { + return nullptr; + } + + OffsetResult thisResult = GetUnretargetedOffsetsFor(*this); + aRect = thisResult.mRect; + + Element* parent = thisResult.mParent; + while (parent && ShouldBeRetargeted(*this, *parent)) { + OffsetResult result = GetUnretargetedOffsetsFor(*parent); + aRect += result.mRect.TopLeft(); + parent = result.mParent; + } - return offsetParent ? offsetParent->AsElement() : nullptr; + return parent; } bool nsGenericHTMLElement::Spellcheck() { diff --git a/testing/web-platform/tests/shadow-dom/offsetParent-across-shadow-boundaries.html b/testing/web-platform/tests/shadow-dom/offsetParent-across-shadow-boundaries.html new file mode 100644 index 0000000000000..5491e2121336d --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/offsetParent-across-shadow-boundaries.html @@ -0,0 +1,190 @@ + + + + + + + + + + + + +
+
+ + +