forked from mirrors/gecko-dev
Bug 1618639 - Make focus-visible match the spec more closely. r=edgar
The spec text has been improved a while ago, so I think we should do this. This keeps the current moz-focusring behavior when the pref is disabled, but when enabled it becomes effectively an alias of focus-visible. Differential Revision: https://phabricator.services.mozilla.com/D96697
This commit is contained in:
parent
a33ff60406
commit
ac6e97d5f9
13 changed files with 155 additions and 110 deletions
|
|
@ -861,7 +861,7 @@ nsresult nsFocusManager::ContentRemoved(Document* aDocument,
|
|||
// 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->GetFocusedElement();
|
||||
Element* content = window->GetFocusedElement();
|
||||
if (content &&
|
||||
nsContentUtils::ContentIsHostIncludingDescendantOf(content, aContent)) {
|
||||
bool shouldShowFocusRing = window->ShouldShowFocusRing();
|
||||
|
|
@ -1177,16 +1177,55 @@ void nsFocusManager::ParentActivated(mozIDOMWindowProxy* aWindow,
|
|||
ActivateOrDeactivate(window, aActive);
|
||||
}
|
||||
|
||||
static bool ShouldMatchFocusVisible(const Element& aElement,
|
||||
int32_t aFocusFlags) {
|
||||
switch (nsFocusManager::GetFocusMoveActionCause(aFocusFlags)) {
|
||||
case InputContextAction::CAUSE_UNKNOWN:
|
||||
case InputContextAction::CAUSE_KEY:
|
||||
nsFocusManager::BlurredElementInfo::BlurredElementInfo(Element& aElement)
|
||||
: mElement(aElement),
|
||||
mHadRing(aElement.State().HasState(NS_EVENT_STATE_FOCUSRING)) {}
|
||||
|
||||
nsFocusManager::BlurredElementInfo::~BlurredElementInfo() = default;
|
||||
|
||||
// https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
|
||||
static bool ShouldMatchFocusVisible(
|
||||
const Element& aElement, int32_t aFocusFlags,
|
||||
const Maybe<nsFocusManager::BlurredElementInfo>& aBlurredElementInfo) {
|
||||
// Any element which supports keyboard input (such as an input element, or any
|
||||
// other element which may trigger a virtual keyboard to be shown on focus if
|
||||
// a physical keyboard is not present) should always match :focus-visible when
|
||||
// focused.
|
||||
{
|
||||
if (aElement.IsHTMLElement(nsGkAtoms::textarea) || aElement.IsEditable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* input = HTMLInputElement::FromNode(aElement)) {
|
||||
if (input->IsSingleLineTextControl()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (nsFocusManager::GetFocusMoveActionCause(aFocusFlags)) {
|
||||
case InputContextAction::CAUSE_KEY:
|
||||
// If the user interacts with the page via the keyboard, the currently
|
||||
// focused element should match :focus-visible (i.e. keyboard usage may
|
||||
// change whether this pseudo-class matches even if it doesn't affect
|
||||
// :focus).
|
||||
return true;
|
||||
case InputContextAction::CAUSE_UNKNOWN:
|
||||
// If the active element matches :focus-visible, and a script causes focus
|
||||
// to move elsewhere, the newly focused element should match
|
||||
// :focus-visible.
|
||||
//
|
||||
// Conversely, if the active element does not match :focus-visible, and a
|
||||
// script causes focus to move elsewhere, the newly focused element should
|
||||
// not match :focus-visible.
|
||||
return !aBlurredElementInfo || aBlurredElementInfo->mHadRing;
|
||||
case InputContextAction::CAUSE_MOUSE:
|
||||
case InputContextAction::CAUSE_TOUCH:
|
||||
case InputContextAction::CAUSE_LONGPRESS:
|
||||
break;
|
||||
// If the user interacts with the page via a pointing device, such that
|
||||
// the focus is moved to a new element which does not support user input,
|
||||
// the newly focused element should not match :focus-visible.
|
||||
return false;
|
||||
case InputContextAction::CAUSE_UNKNOWN_CHROME:
|
||||
case InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT:
|
||||
case InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT:
|
||||
|
|
@ -1201,60 +1240,57 @@ static bool ShouldMatchFocusVisible(const Element& aElement,
|
|||
return false;
|
||||
}
|
||||
|
||||
// On Windows and Linux, focus rings are only shown when the FLAG_SHOWRING flag
|
||||
// is used.
|
||||
static bool ShouldShowFocusRingForElement(Element& aElement, int32_t aFlags) {
|
||||
static bool ShouldFocusRingBeVisible(
|
||||
Element& aElement, int32_t aFlags,
|
||||
const Maybe<nsFocusManager::BlurredElementInfo>& aBlurredElementInfo) {
|
||||
if (aFlags & nsIFocusManager::FLAG_SHOWRING) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool focusVisibleEnabled =
|
||||
StaticPrefs::layout_css_focus_visible_enabled();
|
||||
|
||||
#if defined(XP_MACOSX) || defined(ANDROID)
|
||||
if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
|
||||
return !nsContentUtils::ContentIsLink(&aElement) &&
|
||||
!aElement.IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio);
|
||||
if (!focusVisibleEnabled) {
|
||||
// Preserve historical behavior if the focus visible pseudo-class is not
|
||||
// enabled.
|
||||
if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
|
||||
return !nsContentUtils::ContentIsLink(&aElement) &&
|
||||
!aElement.IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
return focusVisibleEnabled &&
|
||||
ShouldMatchFocusVisible(aElement, aFlags, aBlurredElementInfo);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsFocusManager::NotifyFocusStateChange(nsIContent* aContent,
|
||||
nsIContent* aContentToFocus,
|
||||
bool aWindowShouldShowFocusRing,
|
||||
int32_t aFlags,
|
||||
bool aGettingFocus) {
|
||||
MOZ_ASSERT_IF(aContentToFocus, !aGettingFocus);
|
||||
auto* element = Element::FromNode(aContent);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
void nsFocusManager::NotifyFocusStateChange(
|
||||
Element* aElement, Element* aElementToFocus,
|
||||
bool aWindowShouldShowFocusRing, int32_t aFlags, bool aGettingFocus,
|
||||
const Maybe<BlurredElementInfo>& aBlurredElementInfo) {
|
||||
MOZ_ASSERT_IF(aElementToFocus, !aGettingFocus);
|
||||
nsIContent* commonAncestor = nullptr;
|
||||
if (aContentToFocus && aContentToFocus->IsElement()) {
|
||||
if (aElementToFocus) {
|
||||
commonAncestor = nsContentUtils::GetCommonFlattenedTreeAncestor(
|
||||
aContent, aContentToFocus);
|
||||
aElement, aElementToFocus);
|
||||
}
|
||||
|
||||
if (aGettingFocus) {
|
||||
EventStates eventStateToAdd = NS_EVENT_STATE_FOCUS;
|
||||
if (aWindowShouldShowFocusRing ||
|
||||
ShouldShowFocusRingForElement(*element, aFlags)) {
|
||||
ShouldFocusRingBeVisible(*aElement, aFlags, aBlurredElementInfo)) {
|
||||
eventStateToAdd |= NS_EVENT_STATE_FOCUSRING;
|
||||
}
|
||||
if (aWindowShouldShowFocusRing ||
|
||||
ShouldMatchFocusVisible(*element, aFlags)) {
|
||||
eventStateToAdd |= NS_EVENT_STATE_FOCUS_VISIBLE;
|
||||
}
|
||||
element->AddStates(eventStateToAdd);
|
||||
aElement->AddStates(eventStateToAdd);
|
||||
} else {
|
||||
EventStates eventStateToRemove = NS_EVENT_STATE_FOCUS |
|
||||
NS_EVENT_STATE_FOCUSRING |
|
||||
NS_EVENT_STATE_FOCUS_VISIBLE;
|
||||
element->RemoveStates(eventStateToRemove);
|
||||
EventStates eventStateToRemove =
|
||||
NS_EVENT_STATE_FOCUS | NS_EVENT_STATE_FOCUSRING;
|
||||
aElement->RemoveStates(eventStateToRemove);
|
||||
}
|
||||
|
||||
for (nsIContent* content = aContent; content && content != commonAncestor;
|
||||
for (nsIContent* content = aElement; content && content != commonAncestor;
|
||||
content = content->GetFlattenedTreeParent()) {
|
||||
Element* element = Element::FromNode(content);
|
||||
if (!element) {
|
||||
|
|
@ -1622,7 +1658,10 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
|
|||
isElementInActiveWindow, isElementInFocusedWindow, sendFocusEvent));
|
||||
|
||||
if (sendFocusEvent) {
|
||||
RefPtr<Element> oldFocusedElement = mFocusedElement;
|
||||
Maybe<BlurredElementInfo> blurredInfo;
|
||||
if (mFocusedElement) {
|
||||
blurredInfo.emplace(*mFocusedElement);
|
||||
}
|
||||
// return if blurring fails or the focus changes during the blur
|
||||
if (focusedBrowsingContext) {
|
||||
// if the focus is being moved to another element in the same document,
|
||||
|
|
@ -1659,7 +1698,7 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
|
|||
|
||||
Focus(newWindow, elementToFocus, aFlags, !isElementInFocusedWindow,
|
||||
aFocusChanged, false, aAdjustWidget, focusInOtherContentProcess,
|
||||
oldFocusedElement);
|
||||
blurredInfo);
|
||||
} else {
|
||||
// otherwise, for inactive windows and when the caller cannot steal the
|
||||
// focus, update the node in the window, and raise the window if desired.
|
||||
|
|
@ -2016,10 +2055,10 @@ Element* nsFocusManager::FlushAndCheckIfFocusable(Element* aElement,
|
|||
bool nsFocusManager::Blur(BrowsingContext* aBrowsingContextToClear,
|
||||
BrowsingContext* aAncestorBrowsingContextToFocus,
|
||||
bool aIsLeavingDocument, bool aAdjustWidget,
|
||||
nsIContent* aContentToFocus) {
|
||||
Element* aElementToFocus) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
return BlurImpl(aBrowsingContextToClear, aAncestorBrowsingContextToFocus,
|
||||
aIsLeavingDocument, aAdjustWidget, aContentToFocus);
|
||||
aIsLeavingDocument, aAdjustWidget, aElementToFocus);
|
||||
}
|
||||
mozilla::dom::ContentChild* contentChild =
|
||||
mozilla::dom::ContentChild::GetSingleton();
|
||||
|
|
@ -2062,7 +2101,7 @@ bool nsFocusManager::Blur(BrowsingContext* aBrowsingContextToClear,
|
|||
true);
|
||||
}
|
||||
return BlurImpl(aBrowsingContextToClear, aAncestorBrowsingContextToFocus,
|
||||
aIsLeavingDocument, aAdjustWidget, aContentToFocus);
|
||||
aIsLeavingDocument, aAdjustWidget, aElementToFocus);
|
||||
}
|
||||
if (aBrowsingContextToClear && aBrowsingContextToClear->IsInProcess()) {
|
||||
nsPIDOMWindowOuter* windowToClear = aBrowsingContextToClear->GetDOMWindow();
|
||||
|
|
@ -2104,7 +2143,7 @@ void nsFocusManager::BlurFromOtherProcess(
|
|||
bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
|
||||
BrowsingContext* aAncestorBrowsingContextToFocus,
|
||||
bool aIsLeavingDocument, bool aAdjustWidget,
|
||||
nsIContent* aContentToFocus) {
|
||||
Element* aElementToFocus) {
|
||||
LOGFOCUS(("<<Blur begin>>"));
|
||||
|
||||
// hold a reference to the focused content, which may be null
|
||||
|
|
@ -2181,7 +2220,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
|
|||
element && element->IsInComposedDoc() && !IsNonFocusableRoot(element);
|
||||
if (element) {
|
||||
if (sendBlurEvent) {
|
||||
NotifyFocusStateChange(element, aContentToFocus, shouldShowFocusRing, 0,
|
||||
NotifyFocusStateChange(element, aElementToFocus, shouldShowFocusRing, 0,
|
||||
false);
|
||||
}
|
||||
|
||||
|
|
@ -2257,7 +2296,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
|
|||
}
|
||||
|
||||
SendFocusOrBlurEvent(eBlur, presShell, element->GetComposedDoc(), element,
|
||||
1, false, false, aContentToFocus);
|
||||
1, false, false, aElementToFocus);
|
||||
}
|
||||
|
||||
// if we are leaving the document or the window was lowered, make the caret
|
||||
|
|
@ -2337,11 +2376,11 @@ void nsFocusManager::ActivateRemoteFrameIfNeeded(Element& aElement) {
|
|||
}
|
||||
}
|
||||
|
||||
void nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow, Element* aElement,
|
||||
uint32_t aFlags, bool aIsNewDocument,
|
||||
bool aFocusChanged, bool aWindowRaised,
|
||||
bool aAdjustWidget, bool aFocusInOtherContentProcess,
|
||||
nsIContent* aContentLostFocus) {
|
||||
void nsFocusManager::Focus(
|
||||
nsPIDOMWindowOuter* aWindow, Element* aElement, uint32_t aFlags,
|
||||
bool aIsNewDocument, bool aFocusChanged, bool aWindowRaised,
|
||||
bool aAdjustWidget, bool aFocusInOtherContentProcess,
|
||||
const Maybe<BlurredElementInfo>& aBlurredElementInfo) {
|
||||
LOGFOCUS(("<<Focus begin>>"));
|
||||
|
||||
if (!aWindow) {
|
||||
|
|
@ -2484,7 +2523,8 @@ void nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow, Element* aElement,
|
|||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
if (sendFocusEvent) {
|
||||
NotifyFocusStateChange(aElement, nullptr, aWindow->ShouldShowFocusRing(),
|
||||
aFlags, /* aGettingFocus = */ true);
|
||||
aFlags, /* aGettingFocus = */ true,
|
||||
aBlurredElementInfo);
|
||||
|
||||
// if this is an object/plug-in/remote browser, focus its widget. Note
|
||||
// that we might no longer be in the same document, due to the events we
|
||||
|
|
@ -2513,9 +2553,11 @@ void nsFocusManager::Focus(nsPIDOMWindowOuter* aWindow, Element* aElement,
|
|||
}
|
||||
|
||||
if (!aFocusInOtherContentProcess) {
|
||||
SendFocusOrBlurEvent(eFocus, presShell, aElement->GetComposedDoc(),
|
||||
aElement, aFlags & FOCUSMETHOD_MASK, aWindowRaised,
|
||||
isRefocus, aContentLostFocus);
|
||||
SendFocusOrBlurEvent(
|
||||
eFocus, presShell, aElement->GetComposedDoc(), aElement,
|
||||
aFlags & FOCUSMETHOD_MASK, aWindowRaised, isRefocus,
|
||||
aBlurredElementInfo ? aBlurredElementInfo->mElement.get()
|
||||
: nullptr);
|
||||
}
|
||||
} else {
|
||||
IMEStateManager::OnChangeFocus(presContext, nullptr,
|
||||
|
|
|
|||
|
|
@ -299,6 +299,14 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
|
||||
static void MarkUncollectableForCCGeneration(uint32_t aGeneration);
|
||||
|
||||
struct BlurredElementInfo {
|
||||
const mozilla::OwningNonNull<mozilla::dom::Element> mElement;
|
||||
const bool mHadRing;
|
||||
|
||||
explicit BlurredElementInfo(mozilla::dom::Element&);
|
||||
~BlurredElementInfo();
|
||||
};
|
||||
|
||||
protected:
|
||||
nsFocusManager();
|
||||
~nsFocusManager();
|
||||
|
|
@ -415,7 +423,7 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
bool Blur(mozilla::dom::BrowsingContext* aBrowsingContextToClear,
|
||||
mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
|
||||
bool aIsLeavingDocument, bool aAdjustWidget,
|
||||
nsIContent* aContentToFocus = nullptr);
|
||||
mozilla::dom::Element* aElementToFocus = nullptr);
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
void BlurFromOtherProcess(
|
||||
mozilla::dom::BrowsingContext* aFocusedBrowsingContext,
|
||||
|
|
@ -426,7 +434,7 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
bool BlurImpl(mozilla::dom::BrowsingContext* aBrowsingContextToClear,
|
||||
mozilla::dom::BrowsingContext* aAncestorBrowsingContextToFocus,
|
||||
bool aIsLeavingDocument, bool aAdjustWidget,
|
||||
nsIContent* aContentToFocus);
|
||||
mozilla::dom::Element* aElementToFocus);
|
||||
|
||||
/**
|
||||
* Focus an element in the active window and child frame.
|
||||
|
|
@ -459,7 +467,7 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
uint32_t aFlags, bool aIsNewDocument, bool aFocusChanged,
|
||||
bool aWindowRaised, bool aAdjustWidget,
|
||||
bool aFocusInOtherContentProcess,
|
||||
nsIContent* aContentLostFocus = nullptr);
|
||||
const mozilla::Maybe<BlurredElementInfo>& = mozilla::Nothing());
|
||||
|
||||
/**
|
||||
* Send a focus or blur event at aTarget. It may be added to the delayed
|
||||
|
|
@ -733,18 +741,17 @@ class nsFocusManager final : public nsIFocusManager,
|
|||
nsIContent** aFocusedContent);
|
||||
|
||||
private:
|
||||
// Notify that the focus state of aContent has changed. Note that
|
||||
// we need to pass in whether the window should show a focus ring
|
||||
// before the SetFocusedNode call on it happened when losing focus
|
||||
// and after the SetFocusedNode call when gaining focus, which is
|
||||
// why that information needs to be an explicit argument instead of
|
||||
// just passing in the window and asking it whether it should show
|
||||
// focus rings: in the losing focus case that information could be
|
||||
// wrong..
|
||||
static void NotifyFocusStateChange(nsIContent* aContent,
|
||||
nsIContent* aContentToFocus,
|
||||
bool aWindowShouldShowFocusRing,
|
||||
int32_t aFlags, bool aGettingFocus);
|
||||
// Notify that the focus state of aElement has changed. Note that we need to
|
||||
// pass in whether the window should show a focus ring before the
|
||||
// SetFocusedNode call on it happened when losing focus and after the
|
||||
// SetFocusedNode call when gaining focus, which is why that information needs
|
||||
// to be an explicit argument instead of just passing in the window and asking
|
||||
// it whether it should show focus rings: in the losing focus case that
|
||||
// information could be wrong.
|
||||
static void NotifyFocusStateChange(
|
||||
mozilla::dom::Element* aElement, mozilla::dom::Element* aElementToFocus,
|
||||
bool aWindowShouldShowFocusRing, int32_t aFlags, bool aGettingFocus,
|
||||
const mozilla::Maybe<BlurredElementInfo>& = mozilla::Nothing());
|
||||
|
||||
void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow);
|
||||
|
||||
|
|
|
|||
|
|
@ -287,10 +287,7 @@ class EventStates {
|
|||
#define NS_EVENT_STATE_AUTOFILL NS_DEFINE_EVENT_STATE_MACRO(50)
|
||||
// Element is filled with preview data by Autofill feature.
|
||||
#define NS_EVENT_STATE_AUTOFILL_PREVIEW NS_DEFINE_EVENT_STATE_MACRO(51)
|
||||
// Element matches the :focus-visible pseudo-class.
|
||||
//
|
||||
// TODO(emilio): We should eventually unify this and FOCUSRING.
|
||||
#define NS_EVENT_STATE_FOCUS_VISIBLE NS_DEFINE_EVENT_STATE_MACRO(52)
|
||||
// There's a free bit here.
|
||||
// Modal <dialog> element
|
||||
#define NS_EVENT_STATE_MODAL_DIALOG NS_DEFINE_EVENT_STATE_MACRO(53)
|
||||
// Inert subtrees
|
||||
|
|
@ -331,8 +328,8 @@ class EventStates {
|
|||
NS_EVENT_STATE_DRAGOVER | NS_EVENT_STATE_FOCUS | NS_EVENT_STATE_FOCUSRING | \
|
||||
NS_EVENT_STATE_FOCUS_WITHIN | NS_EVENT_STATE_FULLSCREEN | \
|
||||
NS_EVENT_STATE_HOVER | NS_EVENT_STATE_URLTARGET | \
|
||||
NS_EVENT_STATE_FOCUS_VISIBLE | NS_EVENT_STATE_MODAL_DIALOG | \
|
||||
NS_EVENT_STATE_MOZINERT | NS_EVENT_STATE_TOPMOST_MODAL_DIALOG)
|
||||
NS_EVENT_STATE_MODAL_DIALOG | NS_EVENT_STATE_MOZINERT | \
|
||||
NS_EVENT_STATE_TOPMOST_MODAL_DIALOG)
|
||||
|
||||
#define INTRINSIC_STATES (~EXTERNALLY_MANAGED_STATES)
|
||||
|
||||
|
|
|
|||
|
|
@ -2150,8 +2150,7 @@ void HTMLInputElement::SetFocusState(bool aIsFocused) {
|
|||
return;
|
||||
}
|
||||
|
||||
EventStates focusStates = NS_EVENT_STATE_FOCUS | NS_EVENT_STATE_FOCUSRING |
|
||||
NS_EVENT_STATE_FOCUS_VISIBLE;
|
||||
EventStates focusStates = NS_EVENT_STATE_FOCUS | NS_EVENT_STATE_FOCUSRING;
|
||||
if (aIsFocused) {
|
||||
AddStates(focusStates);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const kFocusVisibleEnabled = SpecialPowers.getBoolPref("layout.css.focus-visible.enabled");
|
||||
|
||||
function snapShot(element) {
|
||||
var rect = element.getBoundingClientRect();
|
||||
adjustedRect = { left: rect.left - 6, top: rect.top - 6,
|
||||
|
|
@ -68,12 +70,12 @@ function runTest()
|
|||
is(getComputedStyle($("l1"), "").outlineWidth, "0px", "appearance on previous list after focus() with :focus");
|
||||
|
||||
synthesizeMouse($("l1"), 4, 4, { });
|
||||
checkFocus($("l1"), expectedVisible, "appearance on list after mouse focus with :moz-focusring");
|
||||
checkFocus($("l1"), expectedVisible && !kFocusVisibleEnabled, "appearance on list after mouse focus with :moz-focusring");
|
||||
synthesizeMouse($("l2"), 4, 4, { });
|
||||
checkFocus($("l2"), true, "appearance on list after mouse focus with :focus");
|
||||
|
||||
synthesizeMouse($("b1"), 4, 4, { });
|
||||
checkFocus($("b1"), !isMac && expectedVisible, "appearance on button after mouse focus with :moz-focusring");
|
||||
checkFocus($("b1"), !isMac && expectedVisible && !kFocusVisibleEnabled, "appearance on button after mouse focus with :moz-focusring");
|
||||
if (navigator.platform.includes("Mac")) {
|
||||
ok(compareSnapshots(snapShot($("b1")), snapShot($("b2")), false)[0], "focus after mouse shows no ring");
|
||||
}
|
||||
|
|
@ -138,12 +140,28 @@ function testHTMLElements(list, isMac, expectedNoRingsOnWin)
|
|||
|
||||
var shouldFocus = !isMac || (elem.className == "canfocus");
|
||||
var ringSize = (shouldFocus ? (expectedNoRingsOnWin ? 2 : 1) : 0) + "px";
|
||||
|
||||
var expectedMatchWithMouse = shouldFocus && !expectedNoRingsOnWin;
|
||||
var mouseRingSize = ringSize;
|
||||
if (shouldFocus && kFocusVisibleEnabled) {
|
||||
var textControl = (function() {
|
||||
if (elem.localName == "textarea")
|
||||
return true;
|
||||
if (elem.localName == "input")
|
||||
return elem.type == "text" || elem.type == "password";
|
||||
return false;
|
||||
}());
|
||||
expectedMatchWithMouse = textControl;
|
||||
mouseRingSize = textControl ? "1px" : "2px";
|
||||
}
|
||||
|
||||
if (elem.localName == "a")
|
||||
ringSize = "0px";
|
||||
mouseRingSize = ringSize = "0px";
|
||||
|
||||
synthesizeMouse(elem, 8, 8, { }, childwin);
|
||||
is(childdoc.activeElement, shouldFocus ? elem : childdoc.body, "mouse click on " + list[e]);
|
||||
is(childwin.getComputedStyle(elem).outlineWidth, ringSize, "mouse click on " + list[e] + " ring");
|
||||
is(childwin.getComputedStyle(elem).outlineWidth, mouseRingSize, "mouse click on " + list[e] + " ring");
|
||||
is(elem.matches(":-moz-focusring"), expectedMatchWithMouse, "mouse click on " + list[e] + " selector");
|
||||
|
||||
if (childdoc.activeElement)
|
||||
childdoc.activeElement.blur();
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ bitflags! {
|
|||
/// Non-standard & undocumented.
|
||||
const IN_INCREMENT_SCRIPT_LEVEL_STATE = 1 << 38;
|
||||
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-focusring
|
||||
///
|
||||
/// But also https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
|
||||
const IN_FOCUSRING_STATE = 1 << 39;
|
||||
/// Non-standard & undocumented.
|
||||
const IN_HANDLER_CLICK_TO_PLAY_STATE = 1 << 40;
|
||||
|
|
@ -133,10 +135,6 @@ bitflags! {
|
|||
const IN_AUTOFILL_STATE = 1 << 50;
|
||||
/// Non-standard & undocumented.
|
||||
const IN_AUTOFILL_PREVIEW_STATE = 1 << 51;
|
||||
/// :focus-visible
|
||||
///
|
||||
/// https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
|
||||
const IN_FOCUS_VISIBLE_STATE = 1 << 52;
|
||||
/// State that dialog element is modal, for centered alignment
|
||||
///
|
||||
/// https://html.spec.whatwg.org/multipage/#centered-alignment
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ macro_rules! apply_non_ts_list {
|
|||
("enabled", Enabled, IN_ENABLED_STATE, _),
|
||||
("focus", Focus, IN_FOCUS_STATE, _),
|
||||
("focus-within", FocusWithin, IN_FOCUS_WITHIN_STATE, _),
|
||||
("focus-visible", FocusVisible, IN_FOCUS_VISIBLE_STATE, _),
|
||||
("focus-visible", FocusVisible, IN_FOCUSRING_STATE, _),
|
||||
("hover", Hover, IN_HOVER_STATE, _),
|
||||
("-moz-drag-over", MozDragOver, IN_DRAGOVER_STATE, _),
|
||||
("target", Target, IN_TARGET_STATE, _),
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
[focus-visible-005.html]
|
||||
[Programmatic focus after click should not match :focus-visible]
|
||||
expected: FAIL
|
||||
|
|
@ -1,7 +1,2 @@
|
|||
[drawFocusIfNeeded_001.html]
|
||||
[drawFocusIfNeeded draws a focus ring.]
|
||||
expected:
|
||||
if os == "mac": PASS
|
||||
if os == "android": PASS
|
||||
FAIL
|
||||
|
||||
prefs: [layout.css.focus-visible.enabled:true]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,2 @@
|
|||
[drawFocusIfNeeded_004.html]
|
||||
[drawFocusIfNeeded does draw a focus ring if the element is in focus.]
|
||||
expected:
|
||||
if os == "android": PASS
|
||||
if os == "mac": PASS
|
||||
FAIL
|
||||
|
||||
prefs: [layout.css.focus-visible.enabled:true]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,2 @@
|
|||
[drawFocusIfNeeded_005.html]
|
||||
[drawFocusIfNeeded does draw a focus ring if the element is in focus and the user activated a particular focus ring.]
|
||||
expected:
|
||||
if os == "mac": PASS
|
||||
if os == "android": PASS
|
||||
FAIL
|
||||
|
||||
prefs: [layout.css.focus-visible.enabled:true]
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
padding: 0px;
|
||||
resize: none;
|
||||
width: 8ch;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
padding: 0px;
|
||||
resize: none;
|
||||
width: 8ch;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
textarea::selection
|
||||
|
|
|
|||
Loading…
Reference in a new issue