diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 445afa0d2..61d32f47a 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -3274,15 +3274,39 @@ nsIDocument::ElementFromPoint(float aX, float aY) return ElementFromPointHelper(aX, aY, false, true); } +void +nsIDocument::ElementsFromPoint(float aX, float aY, + nsTArray>& aElements) +{ + ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements); +} + Element* nsDocument::ElementFromPointHelper(float aX, float aY, bool aIgnoreRootScrollFrame, bool aFlushLayout) { - // As per the the spec, we return null if either coord is negative - if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) { + nsAutoTArray, 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>& 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); @@ -3290,32 +3314,58 @@ nsDocument::ElementFromPointHelper(float aX, float aY, // Make sure the layout information we get is up-to-date, and // ensure we get a root frame (for everything but XUL) - if (aFlushLayout) + if (aFlags & nsIDocument::FLUSH_LAYOUT) { FlushPendingNotifications(Flush_Layout); + } nsIPresShell *ps = GetShell(); if (!ps) { - return nullptr; + return; } nsIFrame *rootFrame = ps->GetRootFrame(); // XUL docs, unlike HTML, have no frame tree until everything's done loading if (!rootFrame) { - return nullptr; // return null to premature XUL callers as a reminder to wait + return; // return null to premature XUL callers as a reminder to wait } - nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, + nsTArray 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 | - (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); - if (!ptFrame) { - return nullptr; + ((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; } - nsIContent* elem = GetContentInThisDocument(ptFrame); - if (elem && !elem->IsElement()) { - elem = elem->GetParent(); + // 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; + } + } } - return elem ? elem->AsElement() : nullptr; } nsresult diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 54f68cc2f..973a45d05 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -1029,8 +1029,12 @@ public: const nsAString& aAttrValue) const override; virtual Element* ElementFromPointHelper(float aX, float aY, - bool aIgnoreRootScrollFrame, - bool aFlushLayout) override; + bool aIgnoreRootScrollFrame, + bool aFlushLayout) override; + + virtual void ElementsFromPointHelper(float aX, float aY, + uint32_t aFlags, + nsTArray>& aElements) override; virtual nsresult NodesFromRectHelper(float aX, float aY, float aTopSize, float aRightSize, diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index ec9480b10..dae027919 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -1672,6 +1672,16 @@ public: 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>& aElements) = 0; + virtual nsresult NodesFromRectHelper(float aX, float aY, float aTopSize, float aRightSize, float aBottomSize, float aLeftSize, @@ -2531,6 +2541,9 @@ public: 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>& aElements); /** * Retrieve the location of the caret position (DOM node and character diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index f78005f3e..5a6468241 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -278,7 +278,7 @@ partial interface Document { // http://dev.w3.org/csswg/cssom-view/#extensions-to-the-document-interface partial interface Document { Element? elementFromPoint (float x, float y); - + sequence elementsFromPoint (float x, float y); CaretPosition? caretPositionFromPoint (float x, float y); readonly attribute Element? scrollingElement;