Bug 1868552 - Refactor nsIContent::IsFocusable for clarity. r=masayuki

Make it be output-only, not having that confusing in-out tab-index
parameter that is special for XUL to become focusable with
-moz-user-focus: normal. Instead, do that explicitly in
nsIFrame::IsFocusable().

Also, call it IsFocusableWithoutStyle(), since that's what it is.

Differential Revision: https://phabricator.services.mozilla.com/D195644
This commit is contained in:
Emilio Cobos Álvarez 2023-12-08 11:34:06 +00:00
parent cc338968d0
commit 633cce158c
21 changed files with 140 additions and 246 deletions

View File

@ -6151,8 +6151,9 @@ nsresult Document::EditingStateChanged() {
getter_AddRefs(focusedWindow));
if (focusedContent) {
nsIFrame* focusedFrame = focusedContent->GetPrimaryFrame();
bool clearFocus = focusedFrame ? !focusedFrame->IsFocusable()
: !focusedContent->IsFocusable();
bool clearFocus = focusedFrame
? !focusedFrame->IsFocusable()
: !focusedContent->IsFocusableWithoutStyle();
if (clearFocus) {
if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
fm->ClearFocus(window);

View File

@ -1032,16 +1032,6 @@ void nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
}
}
bool nsIContent::IsFocusable(int32_t* aTabIndex, bool aWithMouse) {
bool focusable = IsFocusableInternal(aTabIndex, aWithMouse);
// Ensure that the return value and aTabIndex are consistent in the case
// we're in userfocusignored context.
if (focusable || (aTabIndex && *aTabIndex != -1)) {
return focusable;
}
return false;
}
Element* nsIContent::GetAutofocusDelegate(bool aWithMouse) const {
for (nsINode* node = GetFirstChild(); node; node = node->GetNextNode(this)) {
auto* descendant = Element::FromNode(*node);
@ -1068,7 +1058,7 @@ Element* nsIContent::GetFocusDelegate(bool aWithMouse) const {
whereToLook = root;
}
auto IsFocusable = [&](Element* aElement) -> nsIFrame::Focusable {
auto IsFocusable = [&](Element* aElement) -> Focusable {
nsIFrame* frame = aElement->GetPrimaryFrame();
if (!frame) {
@ -1094,7 +1084,7 @@ Element* nsIContent::GetFocusDelegate(bool aWithMouse) const {
return el;
}
} else if (!potentialFocus) {
if (nsIFrame::Focusable focusable = IsFocusable(el)) {
if (Focusable focusable = IsFocusable(el)) {
if (IsHTMLElement(nsGkAtoms::dialog)) {
if (focusable.mTabIndex >= 0) {
// If focusTarget is a dialog element and descendant is sequentially
@ -1134,11 +1124,9 @@ Element* nsIContent::GetFocusDelegate(bool aWithMouse) const {
return potentialFocus;
}
bool nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) {
if (aTabIndex) {
*aTabIndex = -1; // Default, not tabbable
}
return false;
Focusable nsIContent::IsFocusableWithoutStyle(bool aWithMouse) {
// Default, not tabbable
return {};
}
void nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot) {

View File

@ -3377,13 +3377,9 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
int32_t tabIndex = forward ? 1 : 0;
if (startContent) {
nsIFrame* frame = startContent->GetPrimaryFrame();
if (startContent->IsHTMLElement(nsGkAtoms::area)) {
startContent->IsFocusable(&tabIndex);
} else if (frame) {
tabIndex = frame->IsFocusable().mTabIndex;
} else {
startContent->IsFocusable(&tabIndex);
}
tabIndex = (frame && !startContent->IsHTMLElement(nsGkAtoms::area))
? frame->IsFocusable().mTabIndex
: startContent->IsFocusableWithoutStyle().mTabIndex;
// if the current element isn't tabbable, ignore the tabindex and just
// look for the next element. The root content won't have a tabindex
@ -4038,7 +4034,7 @@ nsIContent* nsFocusManager::GetNextTabbableContentInAncestorScopes(
} else if (nsIFrame* frame = startContent->GetPrimaryFrame()) {
tabIndex = frame->IsFocusable().mTabIndex;
} else {
startContent->IsFocusable(&tabIndex);
tabIndex = startContent->IsFocusableWithoutStyle().mTabIndex;
}
nsIContent* contentToFocus = GetNextTabbableContentInScope(
owner, startContent, aOriginalStartContent, aForward, tabIndex,
@ -4214,7 +4210,7 @@ nsresult nsFocusManager::GetNextTabbableContent(
if (iterStartContent == aRootContent) {
if (!aForward) {
frameTraversal->Last();
} else if (aRootContent->IsFocusable()) {
} else if (aRootContent->IsFocusableWithoutStyle()) {
frameTraversal->Next();
}
frame = frameTraversal->CurrentItem();
@ -4282,11 +4278,12 @@ nsresult nsFocusManager::GetNextTabbableContent(
return rv;
}
}
} else {
if (invokerContent && invokerContent->IsFocusable()) {
invokerContent.forget(aResultContent);
return NS_OK;
}
} else if (invokerContent &&
invokerContent->IsFocusableWithoutStyle()) {
// FIXME(emilio): The check above should probably use
// nsIFrame::IsFocusable, not IsFocusableWithoutStyle.
invokerContent.forget(aResultContent);
return NS_OK;
}
}
}
@ -4619,11 +4616,11 @@ nsIContent* nsFocusManager::GetNextTabbableMapArea(bool aForward,
// First see if the the start content is in this map
Maybe<uint32_t> indexOfStartContent =
mapContent->ComputeIndexOf(aStartContent);
int32_t tabIndex;
nsIContent* scanStartContent;
Focusable focusable;
if (indexOfStartContent.isNothing() ||
(aStartContent->IsFocusable(&tabIndex) &&
tabIndex != aCurrentTabIndex)) {
((focusable = aStartContent->IsFocusableWithoutStyle()) &&
focusable.mTabIndex != aCurrentTabIndex)) {
// If aStartContent is in this map we must start iterating past it.
// We skip the case where aStartContent has tabindex == aStartContent
// since the next tab ordered element might be before it
@ -4638,7 +4635,8 @@ nsIContent* nsFocusManager::GetNextTabbableMapArea(bool aForward,
for (nsCOMPtr<nsIContent> areaContent = scanStartContent; areaContent;
areaContent = aForward ? areaContent->GetNextSibling()
: areaContent->GetPreviousSibling()) {
if (areaContent->IsFocusable(&tabIndex) && tabIndex == aCurrentTabIndex) {
focusable = areaContent->IsFocusableWithoutStyle();
if (focusable && focusable.mTabIndex == aCurrentTabIndex) {
return areaContent;
}
}
@ -5459,7 +5457,8 @@ Element* nsFocusManager::GetTheFocusableArea(Element* aTarget,
// HTML areas do not have their own frame, and the img frame we get from
// GetPrimaryFrame() is not relevant as to whether it is focusable or
// not, so we have to do all the relevant checks manually for them.
return frame->IsVisibleConsideringAncestors() && aTarget->IsFocusable()
return frame->IsVisibleConsideringAncestors() &&
aTarget->IsFocusableWithoutStyle()
? aTarget
: nullptr;
}

View File

@ -30,6 +30,16 @@ struct IMEState;
} // namespace widget
} // namespace mozilla
struct Focusable {
bool mFocusable = false;
// The computed tab index:
// < 0 if not tabbable
// == 0 if in normal tab order
// > 0 can be tabbed to in the order specified by this value
int32_t mTabIndex = -1;
explicit operator bool() const { return mFocusable; }
};
// IID for the nsIContent interface
// Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
#define NS_ICONTENT_IID \
@ -280,17 +290,9 @@ class nsIContent : public nsINode {
* Also, depending on either the accessibility.tabfocus pref or
* a system setting (nowadays: Full keyboard access, mac only)
* some widgets may be focusable but removed from the tab order.
* @param [inout, optional] aTabIndex the computed tab index
* In: default tabindex for element (-1 nonfocusable, == 0 focusable)
* Out: computed tabindex
* @param [optional] aTabIndex the computed tab index
* < 0 if not tabbable
* == 0 if in normal tab order
* > 0 can be tabbed to in the order specified by this value
* @return whether the content is focusable via mouse, kbd or script.
*/
bool IsFocusable(int32_t* aTabIndex = nullptr, bool aWithMouse = false);
virtual bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse);
virtual Focusable IsFocusableWithoutStyle(bool aWithMouse = false);
// https://html.spec.whatwg.org/multipage/interaction.html#focus-delegate
mozilla::dom::Element* GetFocusDelegate(bool aWithMouse) const;

View File

@ -211,7 +211,7 @@ nsresult nsStyledElement::BindToTree(BindContext& aContext, nsINode& aParent) {
NS_ENSURE_SUCCESS(rv, rv);
if (HasAttr(nsGkAtoms::autofocus) && aContext.AllowsAutoFocus() &&
(!IsSVGElement() || IsFocusable())) {
(!IsSVGElement() || IsFocusableWithoutStyle())) {
aContext.OwnerDoc().ElementWithAutoFocusInserted(this);
}

View File

@ -1564,24 +1564,23 @@ MOZ_CAN_RUN_SCRIPT static bool IsNextFocusableElementTextControl(
if (!nextElement) {
return false;
}
bool focusable = false;
nextElement->IsHTMLFocusable(false, &focusable, nullptr);
if (!focusable) {
// FIXME: Should probably use nsIFrame::IsFocusable if possible, to account
// for things like visibility: hidden or so.
if (!nextElement->IsFocusableWithoutStyle(false)) {
return false;
}
// Check readonly attribute.
if (nextElement->IsHTMLElement(nsGkAtoms::textarea)) {
HTMLTextAreaElement* textAreaElement =
HTMLTextAreaElement::FromNodeOrNull(nextElement);
auto* textAreaElement = HTMLTextAreaElement::FromNode(nextElement);
return !textAreaElement->ReadOnly();
}
// If neither textarea nor input, what element type?
MOZ_DIAGNOSTIC_ASSERT(nextElement->IsHTMLElement(nsGkAtoms::input));
HTMLInputElement* inputElement =
HTMLInputElement::FromNodeOrNull(nextElement);
auto* inputElement = HTMLInputElement::FromNode(nextElement);
return !inputElement->ReadOnly();
}

View File

@ -98,6 +98,7 @@ bool HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
// cannot focus links if there is no link handler
if (!OwnerDoc()->LinkHandlingEnabled()) {
*aTabIndex = -1;
*aIsFocusable = false;
return false;
}
@ -105,12 +106,8 @@ bool HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
// Links that are in an editable region should never be focusable, even if
// they are in a contenteditable="false" region.
if (nsContentUtils::IsNodeInEditableRegion(this)) {
if (aTabIndex) {
*aTabIndex = -1;
}
*aTabIndex = -1;
*aIsFocusable = false;
return true;
}
@ -119,22 +116,16 @@ bool HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
if (!IsLink()) {
// Not tabbable or focusable without href (bug 17605), unless
// forced to be via presence of nonnegative tabindex attribute
if (aTabIndex) {
*aTabIndex = -1;
}
*aTabIndex = -1;
*aIsFocusable = false;
return false;
}
}
if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
if ((sTabFocusModel & eTabFocus_linksMask) == 0) {
*aTabIndex = -1;
}
*aIsFocusable = true;
return false;
}

View File

@ -492,22 +492,16 @@ bool HTMLImageElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
int32_t tabIndex = TabIndex();
if (IsInComposedDoc() && FindImageMap()) {
if (aTabIndex) {
// Use tab index on individual map areas
*aTabIndex = (sTabFocusModel & eTabFocus_linksMask) ? 0 : -1;
}
// Use tab index on individual map areas.
*aTabIndex = (sTabFocusModel & eTabFocus_linksMask) ? 0 : -1;
// Image map is not focusable itself, but flag as tabbable
// so that image map areas get walked into.
*aIsFocusable = false;
return false;
}
if (aTabIndex) {
// Can be in tab order if tabindex >=0 and form controls are tabbable.
*aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask) ? tabIndex : -1;
}
// Can be in tab order if tabindex >=0 and form controls are tabbable.
*aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask) ? tabIndex : -1;
*aIsFocusable = IsFormControlDefaultFocusable(aWithMouse) &&
(tabIndex >= 0 || GetTabIndexAttrValue().isSome());

View File

@ -2286,6 +2286,8 @@ void nsGenericHTMLElement::Click(CallerType aCallerType) {
bool nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
int32_t* aTabIndex) {
MOZ_ASSERT(aIsFocusable);
MOZ_ASSERT(aTabIndex);
if (ShadowRoot* root = GetShadowRoot()) {
if (root->DelegatesFocus()) {
*aIsFocusable = false;
@ -2293,23 +2295,17 @@ bool nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
}
}
Document* doc = GetComposedDoc();
if (!doc || IsInDesignMode()) {
if (!IsInComposedDoc() || IsInDesignMode()) {
// In designMode documents we only allow focusing the document.
if (aTabIndex) {
*aTabIndex = -1;
}
*aTabIndex = -1;
*aIsFocusable = false;
return true;
}
int32_t tabIndex = TabIndex();
*aTabIndex = TabIndex();
bool disabled = false;
bool disallowOverridingFocusability = true;
Maybe<int32_t> attrVal = GetTabIndexAttrValue();
if (IsEditingHost()) {
// Editable roots should always be focusable.
disallowOverridingFocusability = true;
@ -2319,7 +2315,7 @@ bool nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
if (attrVal.isNothing()) {
// The default value for tabindex should be 0 for editable
// contentEditable roots.
tabIndex = 0;
*aTabIndex = 0;
}
} else {
disallowOverridingFocusability = false;
@ -2327,18 +2323,13 @@ bool nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
// Just check for disabled attribute on form controls
disabled = IsDisabled();
if (disabled) {
tabIndex = -1;
*aTabIndex = -1;
}
}
if (aTabIndex) {
*aTabIndex = tabIndex;
}
// If a tabindex is specified at all, or the default tabindex is 0, we're
// focusable
*aIsFocusable = (tabIndex >= 0 || (!disabled && attrVal.isSome()));
// focusable.
*aIsFocusable = (*aTabIndex >= 0 || (!disabled && attrVal.isSome()));
return disallowOverridingFocusability;
}

View File

@ -334,10 +334,10 @@ class nsGenericHTMLElement : public nsGenericHTMLElementBase {
nsresult BindToTree(BindContext&, nsINode& aParent) override;
void UnbindFromTree(bool aNullParent = true) override;
bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override {
bool isFocusable = false;
IsHTMLFocusable(aWithMouse, &isFocusable, aTabIndex);
return isFocusable;
Focusable IsFocusableWithoutStyle(bool aWithMouse) override {
Focusable result;
IsHTMLFocusable(aWithMouse, &result.mFocusable, &result.mTabIndex);
return result;
}
/**
* Returns true if a subclass is not allowed to override the value returned

View File

@ -612,43 +612,36 @@ void MathMLElement::SetIncrementScriptLevel(bool aIncrementScriptLevel,
int32_t MathMLElement::TabIndexDefault() { return IsLink() ? 0 : -1; }
// XXX Bug 1586011: Share logic with other element classes.
bool MathMLElement::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) {
Focusable MathMLElement::IsFocusableWithoutStyle(bool aWithMouse) {
if (!IsInComposedDoc() || IsInDesignMode()) {
// In designMode documents we only allow focusing the document.
if (aTabIndex) {
*aTabIndex = -1;
}
return false;
return {};
}
int32_t tabIndex = TabIndex();
if (aTabIndex) {
*aTabIndex = tabIndex;
}
if (!IsLink()) {
// If a tabindex is specified at all we're focusable
return GetTabIndexAttrValue().isSome();
if (GetTabIndexAttrValue().isSome()) {
return {true, tabIndex};
}
return {};
}
if (!OwnerDoc()->LinkHandlingEnabled()) {
return false;
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)) {
if (aTabIndex) {
*aTabIndex = -1;
}
return false;
return {};
}
if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
*aTabIndex = -1;
if ((sTabFocusModel & eTabFocus_linksMask) == 0) {
tabIndex = -1;
}
return true;
return {true, tabIndex};
}
already_AddRefed<nsIURI> MathMLElement::GetHrefURI() const {

View File

@ -74,7 +74,7 @@ class MathMLElement final : public MathMLElementBase, public Link {
int32_t TabIndexDefault() final;
bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override;
Focusable IsFocusableWithoutStyle(bool aWithMouse) override;
already_AddRefed<nsIURI> GetHrefURI() const override;
void NodeInfoChanged(Document* aOldDoc) override {

View File

@ -157,23 +157,20 @@ void SVGAElement::UnbindFromTree(bool aNullParent) {
int32_t SVGAElement::TabIndexDefault() { return 0; }
bool SVGAElement::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) {
bool isFocusable = false;
if (IsSVGFocusable(&isFocusable, aTabIndex)) {
return isFocusable;
Focusable SVGAElement::IsFocusableWithoutStyle(bool aWithMouse) {
Focusable result;
if (IsSVGFocusable(&result.mFocusable, &result.mTabIndex)) {
return result;
}
if (!OwnerDoc()->LinkHandlingEnabled()) {
return false;
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)) {
if (aTabIndex) {
*aTabIndex = -1;
}
return false;
return {};
}
if (GetTabIndexAttrValue().isNothing()) {
@ -181,18 +178,13 @@ bool SVGAElement::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) {
if (!IsLink()) {
// Not tabbable or focusable without href (bug 17605), unless
// forced to be via presence of nonnegative tabindex attribute
if (aTabIndex) {
*aTabIndex = -1;
}
return false;
return {};
}
}
if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
*aTabIndex = -1;
if ((sTabFocusModel & eTabFocus_linksMask) == 0) {
result.mTabIndex = -1;
}
return true;
return result;
}
bool SVGAElement::HasHref() const {

View File

@ -53,7 +53,7 @@ class SVGAElement final : public SVGAElementBase,
void UnbindFromTree(bool aNullParent = true) override;
int32_t TabIndexDefault() override;
bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override;
Focusable IsFocusableWithoutStyle(bool aWithMouse) override;
void GetLinkTarget(nsAString& aTarget) override;
already_AddRefed<nsIURI> GetHrefURI() const override;

View File

@ -25,10 +25,7 @@ class SVGDefsElement final : public SVGGraphicsElement {
public:
// defs elements are not focusable.
bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override {
return nsIContent::IsFocusableInternal(aTabIndex, aWithMouse);
}
Focusable IsFocusableWithoutStyle(bool aWithMouse) override { return {}; }
nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
};

View File

@ -154,33 +154,22 @@ bool SVGGraphicsElement::IsSVGFocusable(bool* aIsFocusable,
// MathML elements, see bug 1586011.
if (!IsInComposedDoc() || IsInDesignMode()) {
// In designMode documents we only allow focusing the document.
if (aTabIndex) {
*aTabIndex = -1;
}
*aTabIndex = -1;
*aIsFocusable = false;
return true;
}
int32_t tabIndex = TabIndex();
if (aTabIndex) {
*aTabIndex = tabIndex;
}
*aTabIndex = TabIndex();
// If a tabindex is specified at all, or the default tabindex is 0, we're
// focusable
*aIsFocusable = tabIndex >= 0 || GetTabIndexAttrValue().isSome();
*aIsFocusable = *aTabIndex >= 0 || GetTabIndexAttrValue().isSome();
return false;
}
bool SVGGraphicsElement::IsFocusableInternal(int32_t* aTabIndex,
bool aWithMouse) {
bool isFocusable = false;
IsSVGFocusable(&isFocusable, aTabIndex);
return isFocusable;
Focusable SVGGraphicsElement::IsFocusableWithoutStyle(bool aWithMouse) {
Focusable result;
IsSVGFocusable(&result.mFocusable, &result.mTabIndex);
return result;
}
} // namespace mozilla::dom

View File

@ -34,7 +34,7 @@ class SVGGraphicsElement : public SVGGraphicsElementBase, public SVGTests {
already_AddRefed<SVGMatrix> GetCTM();
already_AddRefed<SVGMatrix> GetScreenCTM();
bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override;
Focusable IsFocusableWithoutStyle(bool aWithMouse) override;
bool IsSVGGraphicsElement() const final { return true; }
using nsINode::Clone;

View File

@ -373,91 +373,54 @@ static bool IsNonList(mozilla::dom::NodeInfo* aNodeInfo) {
!aNodeInfo->Equals(nsGkAtoms::richlistbox);
}
bool nsXULElement::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) {
/*
* Returns true if an element may be focused, and false otherwise. The inout
* argument aTabIndex will be set to the tab order index to be used; -1 for
* elements that should not be part of the tab order and a greater value to
* indicate its tab order.
*
* Confusingly, the supplied value for the aTabIndex argument may indicate
* whether the element may be focused as a result of the -moz-user-focus
* property, where -1 means no and 0 means yes.
*
* For controls, the element cannot be focused and is not part of the tab
* order if it is disabled.
*
* -moz-user-focus is overridden if a tabindex (even -1) is specified.
*
* Specifically, the behaviour for all XUL elements is as follows:
* *aTabIndex = -1 no tabindex Not focusable or tabbable
* *aTabIndex = -1 tabindex="-1" Focusable but not tabbable
* *aTabIndex = -1 tabindex=">=0" Focusable and tabbable
* *aTabIndex >= 0 no tabindex Focusable and tabbable
* *aTabIndex >= 0 tabindex="-1" Focusable but not tabbable
* *aTabIndex >= 0 tabindex=">=0" Focusable and tabbable
*
* If aTabIndex is null, then the tabindex is not computed, and
* true is returned for non-disabled controls and false otherwise.
*/
// elements are not focusable by default
bool shouldFocus = false;
// XUL elements are not focusable unless explicitly opted-into it with
// -moz-user-focus: normal, or the tabindex attribute.
Focusable nsXULElement::IsFocusableWithoutStyle(bool aWithMouse) {
#ifdef XP_MACOSX
// on Mac, mouse interactions only focus the element if it's a list,
// or if it's a remote target, since the remote target must handle
// the focus.
if (aWithMouse && IsNonList(mNodeInfo) &&
!EventStateManager::IsTopLevelRemoteTarget(this)) {
return false;
return {};
}
#endif
bool shouldFocus = false;
nsCOMPtr<nsIDOMXULControlElement> xulControl = AsXULControl();
if (xulControl) {
// a disabled element cannot be focused and is not part of the tab order
bool disabled;
xulControl->GetDisabled(&disabled);
if (disabled) {
if (aTabIndex) *aTabIndex = -1;
return false;
return {};
}
shouldFocus = true;
}
if (aTabIndex) {
Maybe<int32_t> attrVal = GetTabIndexAttrValue();
if (attrVal.isSome()) {
// The tabindex attribute was specified, so the element becomes
// focusable.
shouldFocus = true;
*aTabIndex = attrVal.value();
} else {
// otherwise, if there is no tabindex attribute, just use the value of
// *aTabIndex to indicate focusability. Reset any supplied tabindex to 0.
shouldFocus = *aTabIndex >= 0;
if (shouldFocus) {
*aTabIndex = 0;
}
}
int32_t tabIndex = -1;
if (Maybe<int32_t> attrVal = GetTabIndexAttrValue()) {
// The tabindex attribute was specified, so the element becomes
// focusable.
shouldFocus = true;
tabIndex = attrVal.value();
}
if (xulControl && shouldFocus && sTabFocusModelAppliesToXUL &&
!(sTabFocusModel & eTabFocus_formElementsMask)) {
// By default, the tab focus model doesn't apply to xul element on any
// system but OS X. on OS X we're following it for UI elements (XUL) as
// sTabFocusModel is based on "Full Keyboard Access" system setting (see
// mac/nsILookAndFeel). both textboxes and list elements (i.e. trees and
// list) should always be focusable (textboxes are handled as html:input)
// For compatibility, we only do this for controls, otherwise elements
// like <browser> cannot take this focus.
if (IsNonList(mNodeInfo)) {
*aTabIndex = -1;
}
if (xulControl && shouldFocus && sTabFocusModelAppliesToXUL &&
!(sTabFocusModel & eTabFocus_formElementsMask)) {
// By default, the tab focus model doesn't apply to xul element on any
// system but OS X. on OS X we're following it for UI elements (XUL) as
// sTabFocusModel is based on "Full Keyboard Access" system setting (see
// mac/nsILookAndFeel). both textboxes and list elements (i.e. trees and
// list) should always be focusable (textboxes are handled as html:input)
// For compatibility, we only do this for controls, otherwise elements
// like <browser> cannot take this focus.
if (IsNonList(mNodeInfo)) {
tabIndex = -1;
}
}
return shouldFocus;
return {shouldFocus, tabIndex};
}
bool nsXULElement::HasMenu() {

View File

@ -393,7 +393,7 @@ class nsXULElement : public nsStyledElement {
MOZ_CAN_RUN_SCRIPT void ClickWithInputSource(uint16_t aInputSource,
bool aIsTrustedEvent);
bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override;
Focusable IsFocusableWithoutStyle(bool aWithMouse) override;
NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;

View File

@ -10665,8 +10665,7 @@ bool nsIFrame::IsFocusableDueToScrollFrame() {
return true;
}
nsIFrame::Focusable nsIFrame::IsFocusable(bool aWithMouse,
bool aCheckVisibility) {
Focusable nsIFrame::IsFocusable(bool aWithMouse, bool aCheckVisibility) {
// cannot focus content in print preview mode. Only the root can be focused,
// but that's handled elsewhere.
if (PresContext()->Type() == nsPresContext::eContext_PrintPreview) {
@ -10691,16 +10690,20 @@ nsIFrame::Focusable nsIFrame::IsFocusable(bool aWithMouse,
return {};
}
int32_t tabIndex = -1;
if (ui.UserFocus() != StyleUserFocus::Ignore &&
ui.UserFocus() != StyleUserFocus::None) {
// Pass in default tabindex of -1 for nonfocusable and 0 for focusable
tabIndex = 0;
auto focusable = mContent->IsFocusableWithoutStyle(aWithMouse);
auto* xul = nsXULElement::FromNode(mContent);
if (xul && !xul->GetXULBoolAttr(nsGkAtoms::disabled) &&
xul->GetTabIndexAttrValue().isNothing()) {
// As a legacy special-case, -moz-user-focus controls focusability and
// tabability of XUL elements in some circumstances (which default to
// -moz-user-focus: ignore).
focusable.mFocusable = ui.UserFocus() == StyleUserFocus::Normal;
focusable.mTabIndex =
std::max(focusable.mTabIndex, focusable.mFocusable ? 0 : -1);
}
if (mContent->IsFocusable(&tabIndex, aWithMouse)) {
// If the content is focusable, then we're done.
return {true, tabIndex};
if (focusable) {
return focusable;
}
// If we're focusing with the mouse we never focus scroll areas.
@ -10708,7 +10711,10 @@ nsIFrame::Focusable nsIFrame::IsFocusable(bool aWithMouse,
return {true, 0};
}
return {false, tabIndex};
// FIXME(emilio): some callers rely on somewhat broken return values
// (focusable = false, but non-negative tab-index) from
// IsFocusableWithoutStyle (for image maps in particular).
return focusable;
}
/**

View File

@ -4316,17 +4316,6 @@ class nsIFrame : public nsQueryFrame {
const nsStyleEffects* aEffects,
const nsSize& aSize) const;
struct Focusable {
bool mFocusable = false;
// The computed tab index:
// < 0 if not tabbable
// == 0 if in normal tab order
// > 0 can be tabbed to in the order specified by this value
int32_t mTabIndex = -1;
explicit operator bool() const { return mFocusable; }
};
/**
* Check if this frame is focusable and in the current tab order.
* Tabbable is indicated by a nonnegative tabindex & is a subset of focusable.