From 5e7917e2c8bd50754535652014c1de0054841912 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Sat, 13 Jun 2020 08:23:21 -0400 Subject: Bug 1429656 - Implement ShadowRoot.activeElement Tag #1375 --- dom/base/DocumentOrShadowRoot.cpp | 45 +++++++++++++++++++++++++++++++++++++++ dom/base/DocumentOrShadowRoot.h | 9 ++++++++ dom/base/FragmentOrElement.cpp | 9 ++++++++ dom/base/ShadowRoot.cpp | 6 ++++++ dom/base/ShadowRoot.h | 2 ++ dom/base/nsDocument.cpp | 17 +++------------ dom/base/nsFocusManager.cpp | 7 +++--- dom/base/nsIContent.h | 8 +++++++ dom/webidl/ShadowRoot.webidl | 2 ++ 9 files changed, 88 insertions(+), 17 deletions(-) (limited to 'dom') diff --git a/dom/base/DocumentOrShadowRoot.cpp b/dom/base/DocumentOrShadowRoot.cpp index 8376ab97e..13ee3cb15 100644 --- a/dom/base/DocumentOrShadowRoot.cpp +++ b/dom/base/DocumentOrShadowRoot.cpp @@ -6,6 +6,8 @@ #include "DocumentOrShadowRoot.h" #include "mozilla/dom/StyleSheetList.h" +#include "nsDocument.h" +#include "nsFocusManager.h" #include "ShadowRoot.h" #include "XULDocument.h" @@ -100,5 +102,48 @@ DocumentOrShadowRoot::GetElementsByClassName(const nsAString& aClasses) return nsContentUtils::GetElementsByClassName(&AsNode(), aClasses); } +nsIContent* +DocumentOrShadowRoot::Retarget(nsIContent* aContent) const +{ + for (nsIContent* cur = aContent; + cur; + cur = cur->GetContainingShadowHost()) { + if (cur->SubtreeRoot() == &AsNode()) { + return cur; + } + } + return nullptr; +} + +Element* +DocumentOrShadowRoot::GetRetargetedFocusedElement() +{ + if (nsCOMPtr window = AsNode().OwnerDoc()->GetWindow()) { + nsCOMPtr focusedWindow; + nsIContent* focusedContent = + nsFocusManager::GetFocusedDescendant(window, + false, + getter_AddRefs(focusedWindow)); + // be safe and make sure the element is from this document + if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) { + if (focusedContent->ChromeOnlyAccess()) { + focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent(); + } + + if (focusedContent) { + if (!nsDocument::IsWebComponentsEnabled(focusedContent)) { + return focusedContent->AsElement(); + } + + if (nsIContent* retarget = Retarget(focusedContent)) { + return retarget->AsElement(); + } + } + } + } + + return nullptr; +} + } } diff --git a/dom/base/DocumentOrShadowRoot.h b/dom/base/DocumentOrShadowRoot.h index 59be0c2a9..2092cd54f 100644 --- a/dom/base/DocumentOrShadowRoot.h +++ b/dom/base/DocumentOrShadowRoot.h @@ -116,6 +116,15 @@ public: ~DocumentOrShadowRoot() = default; protected: + nsIContent* Retarget(nsIContent* aContent) const; + + /** + * If focused element's subtree root is this document or shadow root, return + * focused element, otherwise, get the shadow host recursively until the + * shadow host's subtree root is this document or shadow root. + */ + Element* GetRetargetedFocusedElement(); + nsTArray> mStyleSheets; RefPtr mDOMStyleSheets; diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 6fbe04d25..e33d3c63a 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1138,6 +1138,15 @@ FragmentOrElement::GetAssignedSlot() const return slots ? slots->mAssignedSlot.get() : nullptr; } +nsIContent* +nsIContent::GetContainingShadowHost() const +{ + if (mozilla::dom::ShadowRoot* shadow = GetContainingShadow()) { + return shadow->GetHost(); + } + return nullptr; +} + void FragmentOrElement::SetAssignedSlot(HTMLSlotElement* aSlot) { diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp index 4aab9e435..c354e04c1 100644 --- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -461,6 +461,12 @@ ShadowRoot::DistributeAllNodes() DistributionChanged(); } +Element* +ShadowRoot::GetActiveElement() +{ + return GetRetargetedFocusedElement(); +} + void ShadowRoot::GetInnerHTML(nsAString& aInnerHTML) { diff --git a/dom/base/ShadowRoot.h b/dom/base/ShadowRoot.h index eb772d49a..f83a6f774 100644 --- a/dom/base/ShadowRoot.h +++ b/dom/base/ShadowRoot.h @@ -120,6 +120,8 @@ public: // WebIDL methods. using mozilla::dom::DocumentOrShadowRoot::GetElementById; + + Element* GetActiveElement(); void GetInnerHTML(nsAString& aInnerHTML); void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError); void StyleSheetChanged(); diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index a1b1408a5..40bfa97e2 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -3031,20 +3031,9 @@ Element* nsIDocument::GetActiveElement() { // Get the focused element. - if (nsCOMPtr window = GetWindow()) { - nsCOMPtr focusedWindow; - nsIContent* focusedContent = - nsFocusManager::GetFocusedDescendant(window, false, - getter_AddRefs(focusedWindow)); - // be safe and make sure the element is from this document - if (focusedContent && focusedContent->OwnerDoc() == this) { - if (focusedContent->ChromeOnlyAccess()) { - focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent(); - } - if (focusedContent) { - return focusedContent->AsElement(); - } - } + Element* focusedElement = GetRetargetedFocusedElement(); + if (focusedElement) { + return focusedElement; } // No focused element anywhere in this document. Try to get the BODY. diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index c14087c8a..3fc9546e8 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -813,10 +813,11 @@ nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent) if (!window) return NS_OK; - // if the content is currently focused in the window, or is an ancestor - // of the currently focused element, reset the focus within that window. + // if the content is currently focused in the window, or is an + // shadow-including inclusive ancestor of the currently focused element, + // reset the focus within that window. nsIContent* content = window->GetFocusedNode(); - if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) { + if (content && nsContentUtils::ContentIsHostIncludingDescendantOf(content, aContent)) { bool shouldShowFocusRing = window->ShouldShowFocusRing(); window->SetFocusedNode(nullptr); diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index ce0a4e596..101d5e931 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -696,6 +696,14 @@ public: */ virtual mozilla::dom::ShadowRoot *GetContainingShadow() const = 0; + /** + * Gets the shadow host if this content is in a shadow tree. That is, the host + * of |GetContainingShadow|, if its not null. + * + * @return The shadow host, if this is in shadow tree, or null. + */ + nsIContent* GetContainingShadowHost() const; + /** * Gets the assigned slot associated with this content. * diff --git a/dom/webidl/ShadowRoot.webidl b/dom/webidl/ShadowRoot.webidl index 47e6cf5ec..81b50e58b 100644 --- a/dom/webidl/ShadowRoot.webidl +++ b/dom/webidl/ShadowRoot.webidl @@ -20,6 +20,8 @@ enum ShadowRootMode { [Func="nsDocument::IsWebComponentsEnabled"] interface ShadowRoot : DocumentFragment { + readonly attribute Element? activeElement; + // Shadow DOM v1 readonly attribute ShadowRootMode mode; readonly attribute Element host; -- cgit v1.2.3