diff options
author | FranklinDM <mrmineshafter17@gmail.com> | 2023-03-03 21:48:01 +0800 |
---|---|---|
committer | FranklinDM <mrmineshafter17@gmail.com> | 2023-03-04 21:32:11 +0800 |
commit | 6ffdfa793ee9d16de0c1218dbe9188e8a7279f14 (patch) | |
tree | 981815ab10c70de5dd0aea279313d2ee9fbab89d /dom/base | |
parent | fb9484caa34c14a91f3eca8c6465767c2ab00315 (diff) | |
download | uxp-6ffdfa793ee9d16de0c1218dbe9188e8a7279f14.tar.gz |
Issue #2135 - Bug 1430301: Implement ShadowRoot.elementFromPoint/elementsFromPoint
Diffstat (limited to 'dom/base')
-rw-r--r-- | dom/base/DocumentOrShadowRoot.cpp | 118 | ||||
-rw-r--r-- | dom/base/DocumentOrShadowRoot.h | 23 | ||||
-rw-r--r-- | dom/base/nsDocument.cpp | 102 | ||||
-rw-r--r-- | dom/base/nsDocument.h | 18 | ||||
-rw-r--r-- | dom/base/nsIDocument.h | 34 |
5 files changed, 152 insertions, 143 deletions
diff --git a/dom/base/DocumentOrShadowRoot.cpp b/dom/base/DocumentOrShadowRoot.cpp index f8f9d5a28c..876d265cc1 100644 --- a/dom/base/DocumentOrShadowRoot.cpp +++ b/dom/base/DocumentOrShadowRoot.cpp @@ -10,6 +10,8 @@ #include "nsFocusManager.h" #include "ShadowRoot.h" #include "XULDocument.h" +#include "nsLayoutUtils.h" +#include "nsSVGUtils.h" class nsINode; class nsIDocument; @@ -180,5 +182,121 @@ DocumentOrShadowRoot::GetFullscreenElement() return nullptr; } +Element* +DocumentOrShadowRoot::ElementFromPoint(float aX, float aY) +{ + return ElementFromPointHelper(aX, aY, false, true); +} + +void +DocumentOrShadowRoot::ElementsFromPoint(float aX, float aY, + nsTArray<RefPtr<Element>>& aElements) +{ + ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements); +} + +Element* +DocumentOrShadowRoot::ElementFromPointHelper(float aX, float aY, + bool aIgnoreRootScrollFrame, + bool aFlushLayout) +{ + AutoTArray<RefPtr<Element>, 1> elementArray; + ElementsFromPointHelper(aX, aY, + ((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) | + (aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) | + nsIDocument::IS_ELEMENT_FROM_POINT), + elementArray); + if (elementArray.IsEmpty()) { + return nullptr; + } + return elementArray[0]; +} + +void +DocumentOrShadowRoot::ElementsFromPointHelper(float aX, float aY, + uint32_t aFlags, + nsTArray<RefPtr<mozilla::dom::Element>>& aElements) +{ + // As per the the spec, we return null if either coord is negative + if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) { + return; + } + + nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); + nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); + nsPoint pt(x, y); + + nsCOMPtr<nsIDocument> doc = AsNode().OwnerDoc(); + + // Make sure the layout information we get is up-to-date, and + // ensure we get a root frame (for everything but XUL) + if (aFlags & nsIDocument::FLUSH_LAYOUT) { + doc->FlushPendingNotifications(Flush_Layout); + } + + nsIPresShell* ps = doc->GetShell(); + if (!ps) { + return; + } + nsIFrame* rootFrame = ps->GetRootFrame(); + + // XUL docs, unlike HTML, have no frame tree until everything's done loading + if (!rootFrame) { + return; // return null to premature XUL callers as a reminder to wait + } + + nsTArray<nsIFrame*> outFrames; + // Emulate what GetFrameAtPoint does, since we want all the frames under our + // point. + nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames, + nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | + ((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); + + // Dunno when this would ever happen, as we should at least have a root frame under us? + if (outFrames.IsEmpty()) { + return; + } + + // Used to filter out repeated elements in sequence. + nsIContent* lastAdded = nullptr; + + for (uint32_t i = 0; i < outFrames.Length(); i++) { + nsIContent* node = doc->GetContentInThisDocument(outFrames[i]); + + if (!node || !node->IsElement()) { + // If this helper is called via ElementsFromPoint, we need to make sure + // our frame is an element. Otherwise return whatever the top frame is + // even if it isn't the top-painted element. + // SVG 'text' element's SVGTextFrame doesn't respond to hit-testing, so + // if 'node' is a child of such an element then we need to manually defer + // to the parent here. + if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) && + !outFrames[i]->IsSVGText()) { + continue; + } + node = node->GetParent(); + if (node) { + ShadowRoot* shadow = ShadowRoot::FromNode(node); + node = shadow->Host(); + } + } + + //XXXsmaug There is plenty of unspec'ed behavior here + // https://github.com/w3c/webcomponents/issues/735 + // https://github.com/w3c/webcomponents/issues/736 + node = Retarget(node); + + if (node && node != lastAdded) { + aElements.AppendElement(node->AsElement()); + lastAdded = node; + // If this helper is called via ElementFromPoint, just return the first + // element we find. + if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) { + return; + } + } + } +} + } } diff --git a/dom/base/DocumentOrShadowRoot.h b/dom/base/DocumentOrShadowRoot.h index e056078275..962f7ac0b5 100644 --- a/dom/base/DocumentOrShadowRoot.h +++ b/dom/base/DocumentOrShadowRoot.h @@ -117,6 +117,29 @@ public: Element* GetPointerLockElement(); Element* GetFullscreenElement(); + Element* ElementFromPoint(float aX, float aY); + void ElementsFromPoint(float aX, float aY, + nsTArray<RefPtr<mozilla::dom::Element>>& aElements); + + /** + * Helper for nsIDOMDocument::elementFromPoint implementation that allows + * ignoring the scroll frame and/or avoiding layout flushes. + * + * @see nsIDOMWindowUtils::elementFromPoint + */ + Element* ElementFromPointHelper(float aX, float aY, + bool aIgnoreRootScrollFrame, + bool aFlushLayout); + enum ElementsFromPointFlags + { + IGNORE_ROOT_SCROLL_FRAME = 1, + FLUSH_LAYOUT = 2, + IS_ELEMENT_FROM_POINT = 4 + }; + + void ElementsFromPointHelper(float aX, float aY, uint32_t aFlags, + nsTArray<RefPtr<mozilla::dom::Element>>& aElements); + protected: nsIContent* Retarget(nsIContent* aContent) const; diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 562d9e1735..cbb71feaed 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -3120,106 +3120,6 @@ nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn) return NS_OK; } -Element* -nsIDocument::ElementFromPoint(float aX, float aY) -{ - return ElementFromPointHelper(aX, aY, false, true); -} - -void -nsIDocument::ElementsFromPoint(float aX, float aY, - nsTArray<RefPtr<Element>>& aElements) -{ - ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements); -} - -Element* -nsDocument::ElementFromPointHelper(float aX, float aY, - bool aIgnoreRootScrollFrame, - bool aFlushLayout) -{ - AutoTArray<RefPtr<Element>, 1> elementArray; - ElementsFromPointHelper(aX, aY, - ((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) | - (aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) | - nsIDocument::IS_ELEMENT_FROM_POINT), - elementArray); - if (elementArray.IsEmpty()) { - return nullptr; - } - return elementArray[0]; -} - -void -nsDocument::ElementsFromPointHelper(float aX, float aY, - uint32_t aFlags, - nsTArray<RefPtr<mozilla::dom::Element>>& aElements) -{ - // As per the the spec, we return null if either coord is negative - if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) { - return; - } - - nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); - nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); - nsPoint pt(x, y); - - // Make sure the layout information we get is up-to-date, and - // ensure we get a root frame (for everything but XUL) - if (aFlags & nsIDocument::FLUSH_LAYOUT) { - FlushPendingNotifications(Flush_Layout); - } - - nsIPresShell *ps = GetShell(); - if (!ps) { - return; - } - nsIFrame *rootFrame = ps->GetRootFrame(); - - // XUL docs, unlike HTML, have no frame tree until everything's done loading - if (!rootFrame) { - return; // return null to premature XUL callers as a reminder to wait - } - - nsTArray<nsIFrame*> outFrames; - // Emulate what GetFrameAtPoint does, since we want all the frames under our - // point. - nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames, - nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | - ((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); - - // Dunno when this would ever happen, as we should at least have a root frame under us? - if (outFrames.IsEmpty()) { - return; - } - - // Used to filter out repeated elements in sequence. - nsIContent* lastAdded = nullptr; - - for (uint32_t i = 0; i < outFrames.Length(); i++) { - nsIContent* node = GetContentInThisDocument(outFrames[i]); - - if (!node || !node->IsElement()) { - // If this helper is called via ElementsFromPoint, we need to make sure - // our frame is an element. Otherwise return whatever the top frame is - // even if it isn't the top-painted element. - if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT)) { - continue; - } - node = node->GetParent(); - } - if (node && node != lastAdded) { - aElements.AppendElement(node->AsElement()); - lastAdded = node; - // If this helper is called via ElementFromPoint, just return the first - // element we find. - if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) { - return; - } - } - } -} - nsresult nsDocument::NodesFromRectHelper(float aX, float aY, float aTopSize, float aRightSize, @@ -8387,7 +8287,7 @@ nsDocument::DoUnblockOnload() } nsIContent* -nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const +nsIDocument::GetContentInThisDocument(nsIFrame* aFrame) const { for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) { diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 1c10d7b5c3..923fb49ae9 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -692,14 +692,6 @@ public: nsIAtom* aAttrName, const nsAString& aAttrValue) const override; - virtual Element* ElementFromPointHelper(float aX, float aY, - bool aIgnoreRootScrollFrame, - bool aFlushLayout) override; - - virtual void ElementsFromPointHelper(float aX, float aY, - uint32_t aFlags, - nsTArray<RefPtr<mozilla::dom::Element>>& aElements) override; - virtual nsresult NodesFromRectHelper(float aX, float aY, float aTopSize, float aRightSize, float aBottomSize, float aLeftSize, @@ -1312,16 +1304,6 @@ private: nsresult InitCSP(nsIChannel* aChannel); - /** - * Find the (non-anonymous) content in this document for aFrame. It will - * be aFrame's content node if that content is in this document and not - * anonymous. Otherwise, when aFrame is in a subdocument, we use the frame - * element containing the subdocument containing aFrame, and/or find the - * nearest non-anonymous ancestor in this document. - * Returns null if there is no such element. - */ - nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const; - // Just like EnableStyleSheetsForSet, but doesn't check whether // aSheetSet is null and allows the caller to control whether to set // aSheetSet as the preferred set in the CSSLoader. diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index dcaee4fabd..d64827aa6d 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -1811,26 +1811,6 @@ public: nsIAtom* aAttrName, const nsAString& aAttrValue) const = 0; - /** - * Helper for nsIDOMDocument::elementFromPoint implementation that allows - * ignoring the scroll frame and/or avoiding layout flushes. - * - * @see nsIDOMWindowUtils::elementFromPoint - */ - virtual Element* ElementFromPointHelper(float aX, float aY, - bool aIgnoreRootScrollFrame, - bool aFlushLayout) = 0; - - enum ElementsFromPointFlags { - IGNORE_ROOT_SCROLL_FRAME = 1, - FLUSH_LAYOUT = 2, - IS_ELEMENT_FROM_POINT = 4 - }; - - virtual void ElementsFromPointHelper(float aX, float aY, - uint32_t aFlags, - nsTArray<RefPtr<mozilla::dom::Element>>& aElements) = 0; - virtual nsresult NodesFromRectHelper(float aX, float aY, float aTopSize, float aRightSize, float aBottomSize, float aLeftSize, @@ -2664,10 +2644,6 @@ public: void GetPreferredStyleSheetSet(nsAString& aSheetSet); virtual mozilla::dom::DOMStringList* StyleSheetSets() = 0; virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) = 0; - Element* ElementFromPoint(float aX, float aY); - void ElementsFromPoint(float aX, - float aY, - nsTArray<RefPtr<mozilla::dom::Element>>& aElements); /** * Retrieve the location of the caret position (DOM node and character @@ -2835,6 +2811,16 @@ public: bool ModuleScriptsEnabled(); + /** + * Find the (non-anonymous) content in this document for aFrame. It will + * be aFrame's content node if that content is in this document and not + * anonymous. Otherwise, when aFrame is in a subdocument, we use the frame + * element containing the subdocument containing aFrame, and/or find the + * nearest non-anonymous ancestor in this document. + * Returns null if there is no such element. + */ + nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const; + virtual void AddResizeObserver(mozilla::dom::ResizeObserver* aResizeObserver) = 0; virtual void ScheduleResizeObserversNotification() const = 0; |