Bug 1690225 - Remove focus-visible feature flag. r=edgar
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 02 Feb 2021 23:45:25 +0000
changeset 565832 c3d49d6fe8213118ada1c89981ed333dd4b14bce
parent 565831 202e1dac2fee9dd01f81dbdf1a63263c1a27a952
child 565833 3fe7fef2859b73e7cc0c216029b592c866eb77f4
push id38167
push userbtara@mozilla.com
push dateWed, 03 Feb 2021 16:02:50 +0000
treeherdermozilla-central@203b0106b320 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedgar
bugs1690225
milestone87.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1690225 - Remove focus-visible feature flag. r=edgar This shipped in 85, we can remove the feature flag now. Keep :-moz-focusring as an alias to :focus-visible at parse time. Differential Revision: https://phabricator.services.mozilla.com/D103752
dom/base/nsFocusManager.cpp
dom/tests/mochitest/general/test_focusrings.xhtml
layout/mathml/mathml.css
layout/style/res/forms.css
layout/style/res/html.css
layout/style/res/ua.css
layout/svg/svg.css
modules/libpref/init/StaticPrefList.yaml
servo/components/style/gecko/non_ts_pseudo_class_list.rs
servo/components/style/gecko/selector_parser.rs
servo/components/style/gecko/wrapper.rs
testing/web-platform/meta/css/selectors/__dir__.ini
testing/web-platform/meta/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html.ini
testing/web-platform/meta/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html.ini
testing/web-platform/meta/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html.ini
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1155,16 +1155,20 @@ nsFocusManager::BlurredElementInfo::Blur
       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) {
+  // If we were explicitly requested to show the ring, do it.
+  if (aFocusFlags & nsIFocusManager::FLAG_SHOWRING) {
+    return true;
+  }
   // 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;
     }
@@ -1208,57 +1212,32 @@ static bool ShouldMatchFocusVisible(
       // during keyboard input, or such.
       MOZ_ASSERT_UNREACHABLE(
           "These don't get returned by GetFocusMoveActionCause");
       break;
   }
   return false;
 }
 
-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 (!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;
-  }
-#endif
-  return focusVisibleEnabled &&
-         ShouldMatchFocusVisible(aElement, aFlags, aBlurredElementInfo);
-}
-
 /* static */
 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 (aElementToFocus) {
     commonAncestor = nsContentUtils::GetCommonFlattenedTreeAncestor(
         aElement, aElementToFocus);
   }
 
   if (aGettingFocus) {
     EventStates eventStateToAdd = NS_EVENT_STATE_FOCUS;
     if (aWindowShouldShowFocusRing ||
-        ShouldFocusRingBeVisible(*aElement, aFlags, aBlurredElementInfo)) {
+        ShouldMatchFocusVisible(*aElement, aFlags, aBlurredElementInfo)) {
       eventStateToAdd |= NS_EVENT_STATE_FOCUSRING;
     }
     aElement->AddStates(eventStateToAdd);
   } else {
     EventStates eventStateToRemove =
         NS_EVENT_STATE_FOCUS | NS_EVENT_STATE_FOCUSRING;
     aElement->RemoveStates(eventStateToRemove);
   }
--- a/dom/tests/mochitest/general/test_focusrings.xhtml
+++ b/dom/tests/mochitest/general/test_focusrings.xhtml
@@ -13,18 +13,16 @@
 #l2:focus, #b2:focus { outline: 2px solid red; }
 </html:style>
 
 <script>
 <![CDATA[
 
 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,
                    width: rect.width + 12, height: rect.height + 12 }
   return SpecialPowers.snapshotRect(window, adjustedRect, "transparent");
 }
 
 function initTest()
@@ -65,22 +63,22 @@ function runTest()
   checkFocus($("l1"), expectedVisible, "appearance on list after focus() with :moz-focusring");
   $("l2").focus();
 
   checkFocus($("l2"), true, "appearance on list after focus() with :focus");
 
   is(getComputedStyle($("l1"), "").outlineWidth, "0px", "appearance on previous list after focus() with :focus");
 
   synthesizeMouse($("l1"), 4, 4, { });
-  checkFocus($("l1"), expectedVisible && !kFocusVisibleEnabled, "appearance on list after mouse focus with :moz-focusring");
+  checkFocus($("l1"), false, "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 && !kFocusVisibleEnabled, "appearance on button after mouse focus with :moz-focusring");
+  checkFocus($("b1"), false, "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");
   }
 
   synthesizeMouse($("b2"), 4, 4, { });
   checkFocus($("b2"), !isMac, "appearance on button after mouse focus with :focus");
 
   // after a key is pressed, the focus ring will always be visible
@@ -138,17 +136,17 @@ function testHTMLElements(list, isMac, e
     var elem = childdoc.adoptNode(createElement(list[e]));
     container.appendChild(elem);
 
     var shouldFocus = !isMac || (elem.className == "canfocus");
     var ringSize = (shouldFocus ? (expectedNoRingsOnWin ? 2 : 1) : 0) + "px";
 
     var expectedMatchWithMouse = shouldFocus && !expectedNoRingsOnWin;
     var mouseRingSize = ringSize;
-    if (shouldFocus && kFocusVisibleEnabled) {
+    if (shouldFocus) {
       var textControl = (function() {
         if (elem.localName == "textarea")
           return true;
         if (elem.localName == "input")
           return elem.type == "text" || elem.type == "password";
         return false;
       }());
       expectedMatchWithMouse = textControl;
--- a/layout/mathml/mathml.css
+++ b/layout/mathml/mathml.css
@@ -57,17 +57,17 @@ ms[rquote]:after {
 
 /**************************************************************************/
 /* Links and focusable elements                                           */
 /**************************************************************************/
 :any-link {
   text-decoration: none !important;
 }
 
-*:-moz-focusring {
+:focus-visible {
   /* Don't specify the outline-color, we should always use initial value. */
   outline: 1px dotted;
 }
 
 /**************************************************************************/
 /* attributes common to all tags                                          */
 /**************************************************************************/
 
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -487,20 +487,20 @@ input[type=checkbox] {
   background-color: unset;
   color: unset;
 }
 
 input:is([type=radio], [type=checkbox]):is(:disabled, :disabled:active, :disabled:hover:active) {
   cursor: unset;
 }
 
-input:not([type=file], [type=image]):-moz-focusring,
-select:-moz-focusring,
-button:-moz-focusring,
-textarea:-moz-focusring {
+input:not([type=file], [type=image]):focus-visible,
+select:focus-visible,
+button:focus-visible,
+textarea:focus-visible {
   /* These elements can handle outline themselves when themed, so we use
    * outline-style: auto and skip rendering the outline only when themed and
    * the theme allows so */
   outline-style: auto;
 }
 
 input[type=search] {
   box-sizing: border-box;
@@ -632,17 +632,17 @@ input:is([type=reset], [type=button], [t
 
 ::-moz-focus-inner {
   /* Note this padding only affects the -moz-focus-inner ring, not the button itself */
   padding-block: 0;
   padding-inline: 2px;
   border: 1px dotted transparent;
 }
 
-:-moz-focusring::-moz-focus-inner {
+:focus-visible::-moz-focus-inner {
   border-color: currentColor;
 }
 
 :is(:disabled, :disabled:active)::file-selector-button,
 button:is(:disabled, :disabled:active),
 input:is([type=reset], [type=button], [type=submit], [type=color]):is(:disabled, :disabled:active),
 select:is(:disabled, :disabled:active) > button {
   border-style: outset;
@@ -723,17 +723,17 @@ optgroup:before {
 
 /**
  * Set default style for invalid elements.
  */
 :-moz-ui-invalid {
   box-shadow: 0 0 1.5px 1px red;
 }
 
-:-moz-ui-invalid:-moz-focusring {
+:-moz-ui-invalid:focus-visible {
   box-shadow: 0 0 2px 2px rgba(255,0,0,0.4);
 }
 
 output:-moz-ui-invalid {
   box-shadow: initial;
   color: red;
 }
 
--- a/layout/style/res/html.css
+++ b/layout/style/res/html.css
@@ -697,24 +697,24 @@ spacer {
   float: none ! important;
 }
 
 canvas {
   user-select: none;
 }
 
 /* focusable content: anything w/ tabindex >=0 is focusable */
-:-moz-focusring {
+:focus-visible {
   /* Don't specify the outline-color, we should always use initial value. */
    outline: 1px dotted;
 }
 
-iframe:-moz-focusring,
-body:-moz-focusring,
-html:-moz-focusring {
+iframe:focus-visible,
+body:focus-visible,
+html:focus-visible {
   /* These elements historically don't show outlines when focused by default.
    * We could consider changing that if needed. */
   outline-style: none;
 }
 
 /* hidden elements */
 base, basefont, datalist, head, meta, script, style, title,
 noembed, param, template {
--- a/layout/style/res/ua.css
+++ b/layout/style/res/ua.css
@@ -147,18 +147,20 @@
  */
 
 /* Links */
 
 *|*:any-link {
   cursor: pointer;
 }
 
-*|*:any-link:-moz-focusring {
+*|*:any-link:focus-visible {
   /* Don't specify the outline-color, we should always use initial value. */
+  /* TODO(emilio): I think this is redundant, html.css does the same for all
+   * :focus-visible elements. */
   outline: 1px dotted;
 }
 
 /* Inert subtrees */
 *|*:-moz-inert {
   -moz-inert: inert;
 }
 
--- a/layout/svg/svg.css
+++ b/layout/svg/svg.css
@@ -92,17 +92,17 @@ foreignObject {
 
 *|*::-moz-svg-marker-anon-child {
   clip-path: inherit;
   filter: inherit;
   mask: inherit;
   opacity: inherit;
 }
 
-*:-moz-focusring {
+:focus-visible {
   /* Don't specify the outline-color, we should always use initial value. */
   outline: 1px dotted;
 }
 
 /* Make SVG shapes unselectable to avoid triggering AccessibleCaret on tap.
    <mesh> will be supported in bug 1238882. */
 circle, ellipse, line, mesh, path, polygon, polyline, rect {
   user-select: none;
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -964,19 +964,17 @@
   value: true
 #endif
   mirror: always
 
 # Whether we should always enable focus rings after focus was moved by keyboard.
 #
 # This behavior matches both historical and GTK / Windows focus behavior.
 #
-# :focus-visible is intended to provide better heuristics than this, so for now
-# we make this false whenever layout.css.focus-visible.enabled is enabled by
-# default.
+# :focus-visible is intended to provide better heuristics than this.
 - name: browser.display.always_show_rings_after_key_focus
   type: bool
   value: false
   mirror: always
 
 # In theory: 0 = never, 1 = quick, 2 = always, though we always just use it as
 # a bool!
 - name: browser.display.use_document_fonts
@@ -6077,27 +6075,16 @@
 #
 # It never matches regardless.
 - name: layout.css.prefers-color-scheme-no-preference.enabled
   type: RelaxedAtomicBool
   value: false
   mirror: always
   rust: true
 
-# Whether the `:focus-visible` pseudo-class is enabled.
-#
-# NOTE: You probably want to change the default value of
-# browser.display.always_show_rings_after_key_focus whenever you change the
-# default value of this pref.
-- name: layout.css.focus-visible.enabled
-  type: RelaxedAtomicBool
-  value: true
-  mirror: always
-  rust: true
-
 # Whether the `:autofill` pseudo-class is exposed to content.
 - name: layout.css.autofill.enabled
   type: RelaxedAtomicBool
   value: true
   mirror: always
   rust: true
 
 # Whether the `aspect-ratio` in css-sizing-4 is enabled.
--- a/servo/components/style/gecko/non_ts_pseudo_class_list.rs
+++ b/servo/components/style/gecko/non_ts_pseudo_class_list.rs
@@ -50,18 +50,16 @@ macro_rules! apply_non_ts_list {
                 ("target", Target, IN_TARGET_STATE, _),
                 ("indeterminate", Indeterminate, IN_INDETERMINATE_STATE, _),
                 ("-moz-inert", MozInert, IN_MOZINERT_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
                 ("-moz-devtools-highlighted", MozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
                 ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
                 ("fullscreen", Fullscreen, IN_FULLSCREEN_STATE, _),
                 ("-moz-modal-dialog", MozModalDialog, IN_MODAL_DIALOG_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
                 ("-moz-topmost-modal-dialog", MozTopmostModalDialog, IN_TOPMOST_MODAL_DIALOG_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
-                // TODO(emilio): This is inconsistently named (the capital R).
-                ("-moz-focusring", MozFocusRing, IN_FOCUSRING_STATE, _),
                 ("-moz-broken", MozBroken, IN_BROKEN_STATE, _),
                 ("-moz-loading", MozLoading, IN_LOADING_STATE, _),
                 ("-moz-has-dir-attr", MozHasDirAttr, IN_HAS_DIR_ATTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
                 ("-moz-dir-attr-ltr", MozDirAttrLTR, IN_HAS_DIR_ATTR_LTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
                 ("-moz-dir-attr-rtl", MozDirAttrRTL, IN_HAS_DIR_ATTR_RTL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
                 ("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, IN_HAS_DIR_ATTR_LIKE_AUTO_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
 
                 ("-moz-autofill-preview", MozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -97,16 +97,17 @@ impl NonTSPseudoClass {
     pub fn parse_non_functional(name: &str) -> Option<Self> {
         macro_rules! pseudo_class_parse {
             ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
                 match_ignore_ascii_case! { &name,
                     $($css => Some(NonTSPseudoClass::$name),)*
                     "-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen),
                     "-moz-read-only" => Some(NonTSPseudoClass::ReadOnly),
                     "-moz-read-write" => Some(NonTSPseudoClass::ReadWrite),
+                    "-moz-focusring" => Some(NonTSPseudoClass::FocusVisible),
                     "-webkit-autofill" => Some(NonTSPseudoClass::Autofill),
                     _ => None,
                 }
             }
         }
         apply_non_ts_list!(pseudo_class_parse)
     }
 
@@ -131,19 +132,16 @@ impl NonTSPseudoClass {
             }
         }
         apply_non_ts_list!(pseudo_class_check_is_enabled_in)
     }
 
     /// Returns whether the pseudo-class is enabled in content sheets.
     #[inline]
     fn is_enabled_in_content(&self) -> bool {
-        if let NonTSPseudoClass::FocusVisible = *self {
-            return static_prefs::pref!("layout.css.focus-visible.enabled");
-        }
         if let NonTSPseudoClass::Autofill = *self {
             return static_prefs::pref!("layout.css.autofill.enabled");
         }
         !self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME)
     }
 
     /// Get the state flag associated with a pseudo-class, if any.
     pub fn state_flag(&self) -> ElementState {
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -2037,17 +2037,16 @@ impl<'le> ::selectors::Element for Gecko
             NonTSPseudoClass::Optional |
             NonTSPseudoClass::ReadOnly |
             NonTSPseudoClass::ReadWrite |
             NonTSPseudoClass::FocusWithin |
             NonTSPseudoClass::FocusVisible |
             NonTSPseudoClass::MozDragOver |
             NonTSPseudoClass::MozDevtoolsHighlighted |
             NonTSPseudoClass::MozStyleeditorTransitioning |
-            NonTSPseudoClass::MozFocusRing |
             NonTSPseudoClass::MozMathIncrementScriptLevel |
             NonTSPseudoClass::InRange |
             NonTSPseudoClass::OutOfRange |
             NonTSPseudoClass::Default |
             NonTSPseudoClass::MozSubmitInvalid |
             NonTSPseudoClass::MozUIInvalid |
             NonTSPseudoClass::MozMeterOptimum |
             NonTSPseudoClass::MozMeterSubOptimum |
--- a/testing/web-platform/meta/css/selectors/__dir__.ini
+++ b/testing/web-platform/meta/css/selectors/__dir__.ini
@@ -1,3 +1,2 @@
-prefs: [layout.css.focus-visible.enabled:true]
 lsan-disabled: true
 leak-threshold: [default:3276800, tab:460800]
deleted file mode 100644
--- a/testing/web-platform/meta/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[drawFocusIfNeeded_001.html]
-  prefs: [layout.css.focus-visible.enabled:true]
deleted file mode 100644
--- a/testing/web-platform/meta/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[drawFocusIfNeeded_004.html]
-  prefs: [layout.css.focus-visible.enabled:true]
deleted file mode 100644
--- a/testing/web-platform/meta/html/canvas/element/manual/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[drawFocusIfNeeded_005.html]
-  prefs: [layout.css.focus-visible.enabled:true]