From 08796d3b0ff6f3f76daba12a8d40ac566d746c6f Mon Sep 17 00:00:00 2001 From: Fedor Date: Thu, 17 Sep 2020 07:34:32 +0300 Subject: [PATCH] 1429656 - Implement ShadowRoot.activeElement. --- 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 + .../shadow-dom/ShadowRoot-interface.html.ini | 29 ------------ ...activeElement-confirm-return-null.html.ini | 11 ----- .../shadowroot-attributes/test-007.html.ini | 5 --- .../active-element/test-001.html.ini | 5 --- .../active-element/test-002.html.ini | 5 --- 14 files changed, 88 insertions(+), 72 deletions(-) delete mode 100644 testing/web-platform/meta/shadow-dom/ShadowRoot-interface.html.ini delete mode 100644 testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/activeElement-confirm-return-null.html.ini delete mode 100644 testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html.ini delete mode 100644 testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-001.html.ini delete mode 100644 testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-002.html.ini 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 f8b049667..dc4b23f2c 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; diff --git a/testing/web-platform/meta/shadow-dom/ShadowRoot-interface.html.ini b/testing/web-platform/meta/shadow-dom/ShadowRoot-interface.html.ini deleted file mode 100644 index 546e41144..000000000 --- a/testing/web-platform/meta/shadow-dom/ShadowRoot-interface.html.ini +++ /dev/null @@ -1,29 +0,0 @@ -[ShadowRoot-interface.html] - type: testharness - [ShadowRoot.activeElement must return the focused element of the context object when shadow root is open.] - expected: FAIL - - [ShadowRoot.activeElement must return the focused element of the context object when shadow root is closed.] - expected: FAIL - - [ShadowRoot.host must return the shadow host of the context object.] - expected: FAIL - - [ShadowRoot.innerHTML must return the result of the HTML fragment serialization algorithm when shadow root is open.] - expected: FAIL - - [ShadowRoot.innerHTML must return the result of the HTML fragment serialization algorithm when shadow root is closed.] - expected: FAIL - - [ShadowRoot.innerHTML must replace all with the result of invoking the fragment parsing algorithm when shadow root is open.] - expected: FAIL - - [ShadowRoot.innerHTML must replace all with the result of invoking the fragment parsing algorithm when shadow root is closed.] - expected: FAIL - - [ShadowRoot.styleSheets must return a StyleSheetList sequence containing the shadow root style sheets when shadow root is open.] - expected: FAIL - - [ShadowRoot.styleSheets must return a StyleSheetList sequence containing the shadow root style sheets when shadow root is closed.] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/activeElement-confirm-return-null.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/activeElement-confirm-return-null.html.ini deleted file mode 100644 index d82555f73..000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/activeElement-confirm-return-null.html.ini +++ /dev/null @@ -1,11 +0,0 @@ -[activeElement-confirm-return-null.html] - type: testharness - [confirm activeElement return null] - expected: FAIL - - [confirm activeElement return null when there is other element in body] - expected: FAIL - - [confirm activeElement return null when focus on the element in the outer shadow tree] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html.ini deleted file mode 100644 index e621a9779..000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/shadowroot-object/shadowroot-attributes/test-007.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-007.html] - type: testharness - [A_10_01_01_03_01_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-001.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-001.html.ini deleted file mode 100644 index fdcb44343..000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-001.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-001.html] - type: testharness - [A_07_03_01_T01] - expected: FAIL - diff --git a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-002.html.ini b/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-002.html.ini deleted file mode 100644 index e2a9f3f2f..000000000 --- a/testing/web-platform/meta/shadow-dom/untriaged/user-interaction/active-element/test-002.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test-002.html] - type: testharness - [A_07_03_02_T01] - expected: FAIL -