diff --git a/dom/base/Link.cpp b/dom/base/Link.cpp index 55d1717b3653..019b783be673 100644 --- a/dom/base/Link.cpp +++ b/dom/base/Link.cpp @@ -5,6 +5,7 @@ #include "Link.h" #include "mozilla/Components.h" +#include "mozilla/FocusModel.h" #include "mozilla/IHistory.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/dom/BindContext.h" @@ -15,6 +16,7 @@ #include "mozilla/dom/SpeculationRules.h" #include "nsAttrValueInlines.h" #include "nsGkAtoms.h" +#include "nsIContentInlines.h" // for nsINode::IsInDesignMode() #include "nsIURIMutator.h" #include "nsIURIWithSizeOf.h" #include "nsLayoutUtils.h" @@ -393,6 +395,39 @@ void Link::BindToTree(const BindContext& aContext) { ResetLinkState(false); } +Focusable Link::IsLinkFocusableWithoutStyle(IsFocusableFlags aFlags) const { + Element* element = GetElement(); + MOZ_ASSERT(element); + + // Links/Elements cannot be focused if: + // 1. Not in a composed document. + // 2. In designMode (where only the document itself is focusable). + // 3. Document link handling is disabled. + // 4. Node is inside an editable region (Links in an editable region should + // never be focusable, even if in a contenteditable="false" region). + if (!element->IsInComposedDoc() || element->IsInDesignMode() || + !element->OwnerDoc()->LinkHandlingEnabled() || + nsContentUtils::IsNodeInEditableRegion(element)) { + return {}; + } + + int32_t tabIndex = element->TabIndex(); + + // If the element is not actually a link (e.g. without href): + // Not tabbable or focusable unless forced to be via the presence of a + // tabindex attribute (Bug 17605). + if (!element->IsLink()) { + return element->GetTabIndexAttrValue().isSome() ? Focusable{true, tabIndex} + : Focusable{}; + } + + if (!FocusModel::IsTabFocusable(TabFocusableType::Links)) { + tabIndex = -1; + } + + return {true, tabIndex}; +} + void Link::ResetLinkState(bool aNotify, bool aHasHref) { // If we have an href, we should register with the history. mNeedsRegistration = aHasHref; diff --git a/dom/base/Link.h b/dom/base/Link.h index 4b3fe052657d..88a04de99b20 100644 --- a/dom/base/Link.h +++ b/dom/base/Link.h @@ -11,6 +11,7 @@ #include "mozilla/dom/RustTypes.h" #include "nsCOMPtr.h" +#include "nsIContent.h" #include "nsWrapperCache.h" // For nsWrapperCache::FlagsType class nsIURI; @@ -78,6 +79,12 @@ class Link : public nsISupports { void GetPort(nsACString& aPort); void GetHash(nsACString& aHash); + /** + * Helper function for link elements (HTML/SVG/MathML anchor elements) + * to evaluate focusability based on common link-handling constraints. + */ + Focusable IsLinkFocusableWithoutStyle(IsFocusableFlags aFlags) const; + /** * Invalidates any link caching, and resets the state to the default. * diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 8c6c894a9f38..ac97a6a445ff 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -814,6 +814,10 @@ DOMInterfaces = { 'concrete': True, }, +'MathMLElement': { + 'concrete': True, +}, + 'SVGFEFuncAElement': { 'headerFile': 'mozilla/dom/SVGComponentTransferFunctionElement.h', }, diff --git a/dom/html/HTMLAnchorElement.cpp b/dom/html/HTMLAnchorElement.cpp index 1961e7a87c5f..8e616c86ea43 100644 --- a/dom/html/HTMLAnchorElement.cpp +++ b/dom/html/HTMLAnchorElement.cpp @@ -90,36 +90,10 @@ bool HTMLAnchorElement::IsHTMLFocusable(IsFocusableFlags aFlags, return true; } - // cannot focus links if there is no link handler - if (!OwnerDoc()->LinkHandlingEnabled()) { - *aTabIndex = -1; - *aIsFocusable = false; - return false; - } + Focusable focusable = Link::IsLinkFocusableWithoutStyle(aFlags); + *aIsFocusable = focusable.mFocusable; + *aTabIndex = focusable.mTabIndex; - // Links that are in an editable region should never be focusable, even if - // they are in a contenteditable="false" region. - if (nsContentUtils::IsNodeInEditableRegion(this)) { - *aTabIndex = -1; - *aIsFocusable = false; - return true; - } - - if (GetTabIndexAttrValue().isNothing()) { - // check whether we're actually a link - if (!IsLink()) { - // Not tabbable or focusable without href (bug 17605), unless - // forced to be via presence of nonnegative tabindex attribute - *aTabIndex = -1; - *aIsFocusable = false; - return false; - } - } - - if (!FocusModel::IsTabFocusable(TabFocusableType::Links)) { - *aTabIndex = -1; - } - *aIsFocusable = true; return false; } diff --git a/dom/mathml/MathMLAnchorElement.cpp b/dom/mathml/MathMLAnchorElement.cpp new file mode 100644 index 000000000000..b4efd6d27395 --- /dev/null +++ b/dom/mathml/MathMLAnchorElement.cpp @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/MathMLAnchorElement.h" + +#include "mozilla/dom/Document.h" +#include "mozilla/dom/MathMLAnchorElementBinding.h" +#include "nsContentUtils.h" +#include "nsGkAtoms.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(MathMLAnchorElement, MathMLElement) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MathMLAnchorElement) + NS_INTERFACE_MAP_ENTRY(Link) +NS_INTERFACE_MAP_END_INHERITING(MathMLElement) + +NS_IMPL_ADDREF_INHERITED(MathMLAnchorElement, MathMLElement) +NS_IMPL_RELEASE_INHERITED(MathMLAnchorElement, MathMLElement) + +MathMLAnchorElement::MathMLAnchorElement( + already_AddRefed&& aNodeInfo) + : MathMLElement(std::move(aNodeInfo)) {} + +JSObject* MathMLAnchorElement::WrapNode(JSContext* aCx, + JS::Handle aGivenProto) { + return MathMLAnchorElement_Binding::Wrap(aCx, this, aGivenProto); +} + +void MathMLAnchorElement::GetBaseTarget(nsAString& aValue) const { + OwnerDoc()->GetBaseTarget(aValue); +} + +void MathMLAnchorElement::GetLinkTargetImpl(nsAString& aTarget) { + GetAttr(nsGkAtoms::target, aTarget); + if (aTarget.IsEmpty()) { + GetBaseTarget(aTarget); + } +} + +NS_IMPL_ELEMENT_CLONE(MathMLAnchorElement) + +} // namespace mozilla::dom diff --git a/dom/mathml/MathMLAnchorElement.h b/dom/mathml/MathMLAnchorElement.h new file mode 100644 index 000000000000..1ab5562eda17 --- /dev/null +++ b/dom/mathml/MathMLAnchorElement.h @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_MathMLAnchorElement_h +#define mozilla_dom_MathMLAnchorElement_h + +#include "MathMLElement.h" +#include "nsDOMTokenList.h" + +namespace mozilla::dom { + +// FIXME: We should also inherit from Link once we migrate link behavior from +// MathMLElement. Since MathMLElement already inherits from Link, we need to +// prevent diamond inheritance for now. +class MathMLAnchorElement final : public MathMLElement { + public: + explicit MathMLAnchorElement( + already_AddRefed&& aNodeInfo); + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MathMLAnchorElement, MathMLElement) + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + void GetHref(nsAString& aHref) const { + GetURIAttr(nsGkAtoms::href, nullptr, aHref); + } + + void SetHref(const nsAString& aHref, ErrorResult& aRv) { + SetAttr(nsGkAtoms::href, aHref, aRv); + } + + void GetTarget(nsAString& aValue) const { + if (!GetAttr(nsGkAtoms::target, aValue)) { + GetBaseTarget(aValue); + } + } + void SetTarget(const nsAString& aValue, ErrorResult& aRv) { + SetAttr(nsGkAtoms::target, aValue, aRv); + } + + void GetHreflang(DOMString& aValue) const { + GetAttr(nsGkAtoms::hreflang, aValue); + } + void SetHreflang(const nsAString& aValue, mozilla::ErrorResult& rv) { + SetAttr(nsGkAtoms::hreflang, aValue, rv); + } + void GetType(DOMString& aValue) const { GetAttr(nsGkAtoms::type, aValue); } + void SetType(const nsAString& aValue, mozilla::ErrorResult& rv) { + SetAttr(nsGkAtoms::type, aValue, rv); + } + + void GetBaseTarget(nsAString& aValue) const; + void GetLinkTargetImpl(nsAString& aTarget) override; + + nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; + + protected: + virtual ~MathMLAnchorElement() = default; + JSObject* WrapNode(JSContext* aCx, + JS::Handle aGivenProto) override; +}; + +} // namespace mozilla::dom + +#endif diff --git a/dom/mathml/MathMLElement.cpp b/dom/mathml/MathMLElement.cpp index fb14c6f3a988..b10d3ba1fead 100644 --- a/dom/mathml/MathMLElement.cpp +++ b/dom/mathml/MathMLElement.cpp @@ -687,37 +687,8 @@ int32_t MathMLElement::TabIndexDefault() { return mNodeInfo->Equals(nsGkAtoms::a) ? 0 : -1; } -// XXX Bug 1586011: Share logic with other element classes. -Focusable MathMLElement::IsFocusableWithoutStyle(IsFocusableFlags) { - if (!IsInComposedDoc() || IsInDesignMode()) { - // In designMode documents we only allow focusing the document. - return {}; - } - - int32_t tabIndex = TabIndex(); - if (!IsLink()) { - // If a tabindex is specified at all we're focusable - if (GetTabIndexAttrValue().isSome()) { - return {true, tabIndex}; - } - return {}; - } - - if (!OwnerDoc()->LinkHandlingEnabled()) { - return {}; - } - - // Links that are in an editable region should never be focusable, even if - // they are in a contenteditable="false" region. - if (nsContentUtils::IsNodeInEditableRegion(this)) { - return {}; - } - - if (!FocusModel::IsTabFocusable(TabFocusableType::Links)) { - tabIndex = -1; - } - - return {true, tabIndex}; +Focusable MathMLElement::IsFocusableWithoutStyle(IsFocusableFlags aFlags) { + return Link::IsLinkFocusableWithoutStyle(aFlags); } already_AddRefed MathMLElement::GetHrefURI() const { diff --git a/dom/mathml/MathMLElement.h b/dom/mathml/MathMLElement.h index 4c0a879257a5..d01494c0cc4e 100644 --- a/dom/mathml/MathMLElement.h +++ b/dom/mathml/MathMLElement.h @@ -22,7 +22,7 @@ using MathMLElementBase = nsStyledElement; /* * The base class for MathML elements. */ -class MathMLElement final : public MathMLElementBase, public Link { +class MathMLElement : public MathMLElementBase, public Link { public: explicit MathMLElement(already_AddRefed& aNodeInfo); explicit MathMLElement(already_AddRefed aNodeInfo); diff --git a/dom/mathml/MathMLElementFactory.cpp b/dom/mathml/MathMLElementFactory.cpp index 140af89cb02f..9596679f5f91 100644 --- a/dom/mathml/MathMLElementFactory.cpp +++ b/dom/mathml/MathMLElementFactory.cpp @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/StaticPrefs_mathml.h" +#include "mozilla/dom/MathMLAnchorElement.h" #include "mozilla/dom/MathMLElement.h" #include "nsContentCreatorFunctions.h" @@ -11,7 +13,15 @@ using namespace mozilla::dom; nsresult NS_NewMathMLElement( Element** aResult, already_AddRefed aNodeInfo) { RefPtr nodeInfo(aNodeInfo); + auto* nim = nodeInfo->NodeInfoManager(); + + if (nodeInfo->NameAtom() == nsGkAtoms::a && + mozilla::StaticPrefs::mathml_a_element_enabled()) { + NS_ADDREF(*aResult = new (nim) MathMLAnchorElement(nodeInfo.forget())); + return NS_OK; + } + NS_ADDREF(*aResult = new (nim) MathMLElement(nodeInfo.forget())); return NS_OK; } diff --git a/dom/mathml/moz.build b/dom/mathml/moz.build index 807c19322a74..cc839b048868 100644 --- a/dom/mathml/moz.build +++ b/dom/mathml/moz.build @@ -6,10 +6,12 @@ with Files("**"): BUG_COMPONENT = ("Core", "MathML") EXPORTS.mozilla.dom += [ + "MathMLAnchorElement.h", "MathMLElement.h", ] UNIFIED_SOURCES += [ + "MathMLAnchorElement.cpp", "MathMLElement.cpp", "MathMLElementFactory.cpp", ] diff --git a/dom/svg/SVGAElement.cpp b/dom/svg/SVGAElement.cpp index 1b650aaac46a..3d7f1bdd1149 100644 --- a/dom/svg/SVGAElement.cpp +++ b/dom/svg/SVGAElement.cpp @@ -145,34 +145,13 @@ void SVGAElement::UnbindFromTree(UnbindContext& aContext) { int32_t SVGAElement::TabIndexDefault() { return 0; } -Focusable SVGAElement::IsFocusableWithoutStyle(IsFocusableFlags) { +Focusable SVGAElement::IsFocusableWithoutStyle(IsFocusableFlags aFlags) { Focusable result; if (IsSVGFocusable(&result.mFocusable, &result.mTabIndex)) { return result; } - if (!OwnerDoc()->LinkHandlingEnabled()) { - return {}; - } - - // Links that are in an editable region should never be focusable, even if - // they are in a contenteditable="false" region. - if (nsContentUtils::IsNodeInEditableRegion(this)) { - return {}; - } - - if (GetTabIndexAttrValue().isNothing()) { - // check whether we're actually a link - if (!IsLink()) { - // Not tabbable or focusable without href (bug 17605), unless - // forced to be via presence of nonnegative tabindex attribute - return {}; - } - } - if (!FocusModel::IsTabFocusable(TabFocusableType::Links)) { - result.mTabIndex = -1; - } - return result; + return Link::IsLinkFocusableWithoutStyle(aFlags); } bool SVGAElement::HasHref() const { diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js index 7c85d00bbcbe..2e32f7334f40 100644 --- a/dom/tests/mochitest/general/test_interfaces.js +++ b/dom/tests/mochitest/general/test_interfaces.js @@ -979,6 +979,8 @@ let interfaceNamesInGlobalScope = [ // IMPORTANT: Do not change this list without review from a DOM peer! { name: "MIDIPort", android: false }, // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "MathMLAnchorElement", insecureContext: true, nightly: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! { name: "MathMLElement", insecureContext: true }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "MediaCapabilities", insecureContext: true }, diff --git a/dom/webidl/MathMLAnchorElement.webidl b/dom/webidl/MathMLAnchorElement.webidl new file mode 100644 index 000000000000..2ea020c31366 --- /dev/null +++ b/dom/webidl/MathMLAnchorElement.webidl @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * https://w3c.github.io/mathml-core/#dom-mathmlanchorelement + */ + +[ + Exposed=Window, + Pref="mathml.a.element.enabled" +] +interface MathMLAnchorElement : MathMLElement { + [HTMLConstructor] constructor(); + + [CEReactions, SetterThrows] attribute USVString href; + [CEReactions, SetterThrows] attribute DOMString target; +}; + +MathMLAnchorElement includes HyperlinkElementUtils; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 108e148d695a..7e53ad898b14 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -784,6 +784,7 @@ WEBIDL_FILES = [ "Lock.webidl", "LockManager.webidl", "LoginStatus.webidl", + "MathMLAnchorElement.webidl", "MathMLElement.webidl", "MediaCapabilities.webidl", "MediaDebugInfo.webidl", diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index d8fe8f8c6ecd..81cf5f4f07f4 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -12004,6 +12004,12 @@ # Prefs starting with "mathml." #--------------------------------------------------------------------------- +# Enable MathMLAnchorElement dom interface (MathML element). +- name: mathml.a.element.enabled + type: bool + value: @IS_NIGHTLY_BUILD@ + mirror: always + # Enable scale transform for stretchy MathML operators. See bug 414277. - name: mathml.scale_stretchy_operators.enabled type: bool diff --git a/testing/web-platform/meta/mathml/relations/html5-tree/a-hreflang-getter.html.ini b/testing/web-platform/meta/mathml/relations/html5-tree/a-hreflang-getter.html.ini deleted file mode 100644 index 5696e062102f..000000000000 --- a/testing/web-platform/meta/mathml/relations/html5-tree/a-hreflang-getter.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[a-hreflang-getter.html] - [Test anchor's hreflang getter] - expected: FAIL diff --git a/testing/web-platform/meta/mathml/relations/html5-tree/a-hreflang-setter.html.ini b/testing/web-platform/meta/mathml/relations/html5-tree/a-hreflang-setter.html.ini deleted file mode 100644 index 19f2c033fa95..000000000000 --- a/testing/web-platform/meta/mathml/relations/html5-tree/a-hreflang-setter.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[a-hreflang-setter.html] - [Test anchor's hreflang setter] - expected: FAIL diff --git a/testing/web-platform/meta/mathml/relations/html5-tree/a-type-getter-001.html.ini b/testing/web-platform/meta/mathml/relations/html5-tree/a-type-getter-001.html.ini deleted file mode 100644 index 16ba2eccf8c0..000000000000 --- a/testing/web-platform/meta/mathml/relations/html5-tree/a-type-getter-001.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[a-type-getter-001.html] - [Test anchor's type getter when empty] - expected: FAIL diff --git a/testing/web-platform/meta/mathml/relations/html5-tree/a-type-getter-002.html.ini b/testing/web-platform/meta/mathml/relations/html5-tree/a-type-getter-002.html.ini deleted file mode 100644 index f8134e595a92..000000000000 --- a/testing/web-platform/meta/mathml/relations/html5-tree/a-type-getter-002.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[a-type-getter-002.html] - [Test anchor's type getter] - expected: FAIL diff --git a/testing/web-platform/meta/mathml/relations/html5-tree/a-type-setter.html.ini b/testing/web-platform/meta/mathml/relations/html5-tree/a-type-setter.html.ini deleted file mode 100644 index 103ef77e8fbe..000000000000 --- a/testing/web-platform/meta/mathml/relations/html5-tree/a-type-setter.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[a-type-setter.html] - [Test anchor's type setter] - expected: FAIL diff --git a/testing/web-platform/meta/mathml/relations/html5-tree/anchor-hyperlinkutils-getter-setter.html.ini b/testing/web-platform/meta/mathml/relations/html5-tree/anchor-hyperlinkutils-getter-setter.html.ini deleted file mode 100644 index 7614b5e6de85..000000000000 --- a/testing/web-platform/meta/mathml/relations/html5-tree/anchor-hyperlinkutils-getter-setter.html.ini +++ /dev/null @@ -1,42 +0,0 @@ -[anchor-hyperlinkutils-getter-setter.html] - [Getter and setter for attribute of anchor element(0): hash] - expected: FAIL - - [Getter and setter for attribute of anchor element(1): hash] - expected: FAIL - - [Getter and setter for attribute of anchor element(2): host] - expected: FAIL - - [Getter and setter for attribute of anchor element(3): hostname] - expected: FAIL - - [Getter and setter for attribute of anchor element(5): password] - expected: FAIL - - [Getter and setter for attribute of anchor element(6): pathname] - expected: FAIL - - [Getter and setter for attribute of anchor element(7): port] - expected: FAIL - - [Getter and setter for attribute of anchor element(8): protocol] - expected: FAIL - - [Getter and setter for attribute of anchor element(9): protocol] - expected: FAIL - - [Getter and setter for attribute of anchor element(10): search] - expected: FAIL - - [Getter and setter for attribute of anchor element(11): search] - expected: FAIL - - [Getter and setter for attribute of anchor element(12): search] - expected: FAIL - - [Getter and setter for attribute of anchor element(13): search] - expected: FAIL - - [Getter and setter for attribute of anchor element(14): username] - expected: FAIL diff --git a/testing/web-platform/meta/mathml/relations/html5-tree/anchor-hyperlinkutils-getter.html.ini b/testing/web-platform/meta/mathml/relations/html5-tree/anchor-hyperlinkutils-getter.html.ini deleted file mode 100644 index 3b44a3f24c77..000000000000 --- a/testing/web-platform/meta/mathml/relations/html5-tree/anchor-hyperlinkutils-getter.html.ini +++ /dev/null @@ -1,30 +0,0 @@ -[anchor-hyperlinkutils-getter.html] - [Getter for attribute of anchor element(0): search] - expected: FAIL - - [Getter for attribute of anchor element(1): hash] - expected: FAIL - - [Getter for attribute of anchor element(2): host] - expected: FAIL - - [Getter for attribute of anchor element(3): hostname] - expected: FAIL - - [Getter for attribute of anchor element(4): href] - expected: FAIL - - [Getter for attribute of anchor element(5): password] - expected: FAIL - - [Getter for attribute of anchor element(6): pathname] - expected: FAIL - - [Getter for attribute of anchor element(7): port] - expected: FAIL - - [Getter for attribute of anchor element(8): protocol] - expected: FAIL - - [Getter for attribute of anchor element(9): username] - expected: FAIL diff --git a/testing/web-platform/meta/mathml/relations/html5-tree/href-navigation.html.ini b/testing/web-platform/meta/mathml/relations/html5-tree/href-navigation.html.ini deleted file mode 100644 index 75332544ac39..000000000000 --- a/testing/web-platform/meta/mathml/relations/html5-tree/href-navigation.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[href-navigation.html] - expected: - if (os == "android") and debug: [ERROR, CRASH] - if (os == "android") and not debug: [ERROR, CRASH] - ERROR diff --git a/testing/web-platform/tests/mathml/relations/html5-tree/href-target-base.html b/testing/web-platform/tests/mathml/relations/html5-tree/href-target-base.html new file mode 100644 index 000000000000..7601043e8181 --- /dev/null +++ b/testing/web-platform/tests/mathml/relations/html5-tree/href-target-base.html @@ -0,0 +1,51 @@ + + +MathML a element target attribute navigation and base element fallback + + + + + + + + + + + + + + Link Empty Target + + + + \ No newline at end of file