Bug 1827516 - Fix retargeting of UAWidget.elementFromPoint(). r=smaug

The continue in GetContentInThisDocument meant that if we call that in a
UA widget we'd return null...

This breaks the following check:

  https://searchfox.org/mozilla-central/rev/31f5847a4494b3646edabbdd7ea39cb88509afe2/toolkit/content/widgets/videocontrols.js#1373-1381

Which was introduced in bug 1513600 to fix precisely this bug...

Depends on D175714

Differential Revision: https://phabricator.services.mozilla.com/D175715
This commit is contained in:
Emilio Cobos Álvarez 2023-04-18 09:23:06 +00:00
Родитель df7453527a
Коммит 452fa41d65
5 изменённых файлов: 57 добавлений и 35 удалений

Просмотреть файл

@ -11652,7 +11652,9 @@ nsIContent* Document::GetContentInThisDocument(nsIFrame* aFrame) const {
for (nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
nsIContent* content = f->GetContent();
if (!content || content->IsInNativeAnonymousSubtree()) continue;
if (!content) {
continue;
}
if (content->OwnerDoc() == this) {
return content;

Просмотреть файл

@ -285,8 +285,8 @@ already_AddRefed<nsContentList> DocumentOrShadowRoot::GetElementsByClassName(
return nsContentUtils::GetElementsByClassName(&AsNode(), aClasses);
}
nsIContent* DocumentOrShadowRoot::Retarget(nsIContent* aContent) const {
for (nsIContent* cur = aContent; cur; cur = cur->GetContainingShadowHost()) {
nsINode* DocumentOrShadowRoot::Retarget(nsINode* aNode) const {
for (nsINode* cur = aNode; cur; cur = cur->GetContainingShadowHost()) {
if (cur->SubtreeRoot() == &AsNode()) {
return cur;
}
@ -299,7 +299,7 @@ Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
if (!content) {
return nullptr;
}
if (nsIContent* retarget = Retarget(content)) {
if (nsINode* retarget = Retarget(content)) {
return retarget->AsElement();
}
return nullptr;
@ -308,15 +308,7 @@ Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
Element* DocumentOrShadowRoot::GetPointerLockElement() {
nsCOMPtr<Element> pointerLockedElement =
PointerLockManager::GetLockedElement();
if (!pointerLockedElement) {
return nullptr;
}
nsIContent* retargetedPointerLockedElement = Retarget(pointerLockedElement);
return retargetedPointerLockedElement &&
retargetedPointerLockedElement->IsElement()
? retargetedPointerLockedElement->AsElement()
: nullptr;
return Element::FromNodeOrNull(Retarget(pointerLockedElement));
}
Element* DocumentOrShadowRoot::GetFullscreenElement() const {
@ -327,13 +319,7 @@ Element* DocumentOrShadowRoot::GetFullscreenElement() const {
Element* element = AsNode().OwnerDoc()->GetUnretargetedFullscreenElement();
NS_ASSERTION(!element || element->State().HasState(ElementState::FULLSCREEN),
"Fullscreen element should have fullscreen styles applied");
nsIContent* retargeted = Retarget(element);
if (retargeted && retargeted->IsElement()) {
return retargeted->AsElement();
}
return nullptr;
return Element::FromNodeOrNull(Retarget(element));
}
namespace {
@ -359,16 +345,16 @@ enum class PerformRetargeting {
};
template <typename NodeOrElement>
NodeOrElement* CastTo(nsIContent* aContent);
NodeOrElement* CastTo(nsINode*);
template <>
Element* CastTo<Element>(nsIContent* aContent) {
return aContent->AsElement();
Element* CastTo<Element>(nsINode* aNode) {
return aNode->AsElement();
}
template <>
nsINode* CastTo<nsINode>(nsIContent* aContent) {
return aContent;
nsINode* CastTo<nsINode>(nsINode* aNode) {
return aNode;
}
template <typename NodeOrElement>
@ -413,12 +399,23 @@ static void QueryNodesFromRect(DocumentOrShadowRoot& aRoot, const nsRect& aRect,
aOptions);
for (nsIFrame* frame : frames) {
nsIContent* content = doc->GetContentInThisDocument(frame);
if (!content) {
nsINode* node = doc->GetContentInThisDocument(frame);
while (node && node->IsInNativeAnonymousSubtree()) {
nsIContent* root = node->GetClosestNativeAnonymousSubtreeRoot();
MOZ_ASSERT(root, "content is connected");
MOZ_ASSERT(root->IsRootOfNativeAnonymousSubtree(), "wat");
if (root == &aRoot.AsNode()) {
// If we're in the anonymous subtree root we care about, don't retarget.
break;
}
node = root->GetParentOrShadowHostNode();
}
if (!node) {
continue;
}
if (returningElements && !content->IsElement()) {
if (returningElements && !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.
@ -429,9 +426,9 @@ static void QueryNodesFromRect(DocumentOrShadowRoot& aRoot, const nsRect& aRect,
continue;
}
content = content->GetParent();
if (ShadowRoot* shadow = ShadowRoot::FromNodeOrNull(content)) {
content = shadow->Host();
node = node->GetParent();
if (ShadowRoot* shadow = ShadowRoot::FromNodeOrNull(node)) {
node = shadow->Host();
}
}
@ -439,11 +436,11 @@ static void QueryNodesFromRect(DocumentOrShadowRoot& aRoot, const nsRect& aRect,
// https://github.com/w3c/webcomponents/issues/735
// https://github.com/w3c/webcomponents/issues/736
if (retargeting) {
content = aRoot.Retarget(content);
node = aRoot.Retarget(node);
}
if (content && content != aNodes.SafeLastElement(nullptr)) {
aNodes.AppendElement(CastTo<NodeOrElement>(content));
if (node && node != aNodes.SafeLastElement(nullptr)) {
aNodes.AppendElement(CastTo<NodeOrElement>(node));
if (aMultiple == Multiple::No) {
return;
}

Просмотреть файл

@ -208,7 +208,7 @@ class DocumentOrShadowRoot : public RadioGroupManager {
MOZ_CAN_RUN_SCRIPT
void GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations);
nsIContent* Retarget(nsIContent* aContent) const;
nsINode* Retarget(nsINode*) const;
void OnSetAdoptedStyleSheets(StyleSheet&, uint32_t aIndex, ErrorResult&);
void OnDeleteAdoptedStyleSheets(StyleSheet&, uint32_t aIndex, ErrorResult&);

Просмотреть файл

@ -32,6 +32,7 @@ support-files =
../../widgets/panel-item.css
../../widgets/panel-list.js
../../widgets/panel-list.css
[test_ua_widget_elementFromPoint.html]
[test_ua_widget_sandbox.html]
[test_ua_widget_unbind.html]
[test_videocontrols.html]

Просмотреть файл

@ -0,0 +1,22 @@
<!doctype html>
<title>UA Widget getElementFromPoint</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
<div id="host" style="width: 100px; height: 100px;"></div>
<script>
const host = document.getElementById("host");
SpecialPowers.wrap(host.attachShadow({ mode: "open"})).setIsUAWidget();
host.shadowRoot.innerHTML = `
<div style="width: 100px; height: 100px; background-color: green;"></div>
`;
let hostRect = host.getBoundingClientRect();
let point = {
x: hostRect.x + 50,
y: hostRect.y + 50,
};
is(document.elementFromPoint(point.x, point.y), host,
"Host should be found from the document");
is(host.shadowRoot.elementFromPoint(point.x, point.y), host.shadowRoot.firstElementChild,
"Should not have retargeted UA widget content to host unnecessarily");
</script>