Bug 1623837 - Don't prevent focus switching for disabled form controls. r=masayuki,NeilDeakin
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 26 Mar 2020 18:23:36 +0000
changeset 520619 9f779fe80cca638f57ee83f57cfc874336d6a255
parent 520618 a74b5711286bdab2f7d72eb7d9e25775cae368f5
child 520620 e382ca4423f33a96356a376409bfcc7563791d71
push id37254
push usernerli@mozilla.com
push dateFri, 27 Mar 2020 04:48:07 +0000
treeherdermozilla-central@2d758b42bd73 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki, NeilDeakin
bugs1623837, 375008
milestone76.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 1623837 - Don't prevent focus switching for disabled form controls. r=masayuki,NeilDeakin Otherwise the user can select, but if you're in an iframe the iframe won't be correctly focused. This also matches other browsers, see: data:text/html,<input autofocus><input disabled value=abc> data:text/html,<input autofocus><button disabled>Foo</button> And so on. This effectively undoes bug 375008. We could make this more targeted somehow, but I don't think it's worth it. Differential Revision: https://phabricator.services.mozilla.com/D67597
dom/base/test/test_copypaste_disabled.html
dom/events/EventStateManager.cpp
dom/events/test/mochitest.ini
dom/events/test/test_focus_disabled.html
--- a/dom/base/test/test_copypaste_disabled.html
+++ b/dom/base/test/test_copypaste_disabled.html
@@ -1,29 +1,30 @@
 <!doctype html>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <script src="/tests/SimpleTest/EventUtils.js"></script>
 <script src="copypaste.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
-<style>
-@font-face {
-  font-family: Ahem;
-  src: url("Ahem.ttf");
-}
-body { font-family: Ahem; font-size: 20px; }
-input, textarea {
-  font: inherit;
-  -moz-appearance: none;
-  padding: 0;
-  border: 0;
-  scrollbar-width: none;
-}
-</style>
-
-<input id="disabled-input" disabled value="abcd"> efgh <br> <textarea rows=1 id="disabled-textarea" disabled>ijkl</textarea> mnop
+<div id="content">
+  <style>
+  @font-face {
+    font-family: Ahem;
+    src: url("Ahem.ttf");
+  }
+  body { font-family: Ahem; font-size: 20px; margin: 0; }
+  input, textarea {
+    font: inherit;
+    -moz-appearance: none;
+    padding: 0;
+    border: 0;
+    scrollbar-width: none;
+  }
+  </style>
+  <input id="disabled-input" disabled value="abcd"> efgh <br> <textarea rows=1 id="disabled-textarea" disabled>ijkl</textarea> mnop <br>
+</div>
 
 <script>
 function dragSelect(e, x1, x2, x3) {
   dir = x2 > x1 ? 1 : -1;
   synthesizeMouse(e, x1, 5, { type: "mousedown" });
   synthesizeMouse(e, x1 + dir, 5, { type: "mousemove" });
   if (x3)
     synthesizeMouse(e, x3, 5, { type: "mousemove" });
@@ -73,18 +74,35 @@ SimpleTest.waitForFocus(async function()
         ? data.value
         : data.value.QueryInterface(SpecialPowers.Ci.nsISupportsString).data,
       expected,
       mime + " value in the clipboard"
     );
     return data.value;
   }
 
-  for (let id of ["disabled-input", "disabled-textarea"]) {
-    let element = document.getElementById(id);
-    dragSelect(element, 0, 60);
-    await copySelectionToClipboard();
-    testClipboardValue("text/unicode", element.value.substr(0, 3));
+  async function runTestsOn(doc) {
+    for (let id of ["disabled-input", "disabled-textarea"]) {
+      let element = doc.getElementById(id);
+      dragSelect(element, 0, 60);
+      await copySelectionToClipboard();
+      testClipboardValue("text/unicode", element.value.substr(0, 3));
+    }
   }
 
+  await runTestsOn(document)
+
+  let iframe = document.createElement("iframe");
+  iframe.setAttribute("frameborder", "0");
+  iframe.srcdoc = `<!doctype html>${document.getElementById("content").outerHTML}`;
+  let iframeLoad = new Promise(resolve => {
+    iframe.addEventListener("load", resolve, { once: true });
+  });
+  document.body.appendChild(iframe);
+
+  await iframeLoad;
+  iframe.width = window.innerWidth;
+  iframe.height = window.innerHeight;
+  await runTestsOn(iframe.contentDocument);
+
   SimpleTest.finish();
 });
 </script>
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3143,39 +3143,32 @@ nsresult EventStateManager::PostHandleEv
         if (mCurrentTarget) {
           mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(newFocus));
           const nsStyleUI* ui = mCurrentTarget->StyleUI();
           activeContent = mCurrentTarget->GetContent();
 
           // In some cases, we do not want to even blur the current focused
           // element. Those cases are:
           // 1. -moz-user-focus CSS property is set to 'ignore';
-          // 2. Element with NS_EVENT_STATE_DISABLED
-          //    (aka :disabled pseudo-class for HTML element);
-          // 3. XUL control element has the disabled property set to 'true'.
+          // 2. XUL control element has the disabled property set to 'true'.
           //
           // We can't use nsIFrame::IsFocusable() because we want to blur when
           // we click on a visibility: none element.
           // We can't use nsIContent::IsFocusable() because we want to blur when
           // we click on a non-focusable element like a <div>.
           // We have to use |aEvent->mTarget| to not make sure we do not check
           // an anonymous node of the targeted element.
           suppressBlur = (ui->mUserFocus == StyleUserFocus::Ignore);
 
           nsCOMPtr<Element> element = do_QueryInterface(aEvent->mTarget);
-          if (!suppressBlur) {
-            suppressBlur =
-                element && element->State().HasState(NS_EVENT_STATE_DISABLED);
-          }
-
           if (!suppressBlur && element) {
             nsCOMPtr<nsIDOMXULControlElement> xulControl =
                 element->AsXULControl();
             if (xulControl) {
-              bool disabled;
+              bool disabled = false;
               xulControl->GetDisabled(&disabled);
               suppressBlur = disabled;
             }
           }
         }
 
         if (!suppressBlur) {
           suppressBlur = nsContentUtils::IsUserFocusIgnored(activeContent);
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -177,17 +177,16 @@ skip-if = toolkit == 'android' #CRASH_DU
 [test_eventctors_sensors.html]
 [test_disabled_events.html]
 [test_event_screenXY_in_cross_origin_iframe.html]
 support-files =
   file_event_screenXY.html
   !/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
 [test_eventhandler_scoping.html]
 [test_eventTimeStamp.html]
-[test_focus_disabled.html]
 [test_focus_abspos.html]
 [test_legacy_event.html]
 [test_legacy_non-primary_click.html]
 [test_legacy_touch_api.html]
 [test_messageEvent.html]
 [test_messageEvent_init.html]
 [test_moz_mouse_pixel_scroll_event.html]
 [test_offsetxy.html]
deleted file mode 100644
--- a/dom/events/test/test_focus_disabled.html
+++ /dev/null
@@ -1,125 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=375008
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 375008</title>
-  <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=375008">Mozilla Bug 375008</a>
-<p id="display"></p>
-<div id="content">
-  <div id='not-focusable'>
-    <!-- Disabled elements -->
-    <button hidden disabled>foo</button>
-    <input hidden disabled>
-    <fieldset hidden disabled>foo</fieldset>
-    <select hidden disabled><option>foo</option></select>
-    <textarea hidden disabled></textarea>
-    <optgroup hidden disabled><option>foo</option></optgroup>
-    <option hidden disabled>foo</option>
-  </div>
-
-  <div id='focusable'>
-    <button hidden>foo</button>
-    <input hidden>
-    <select hidden><option>foo</option></select>
-    <textarea hidden></textarea>
-
-    <!-- Those elements are not focusable by default. -->
-    <fieldset tabindex=1 hidden>foo</fieldset>
-    <optgroup tabindex=1 hidden><option>foo</option></optgroup>
-    <option tabindex=1 hidden>foo</option>
-  </div>
-
-  <!-- Hidden elements, they have a frame but focus will go through them. -->
-  <div id='hidden' style='visibility: hidden;'>
-    <button hidden>foo</button>
-    <input hidden>
-    <fieldset hidden>foo</fieldset>
-    <select hidden><option>foo</option></select>
-    <textarea hidden></textarea>
-    <optgroup hidden><option>foo</option></optgroup>
-    <option hidden>foo</option>
-  </div>
-
-  <div>
-    <input id='witness'>
-  </div>
-
-</div>
-<pre id="test">
-<script type="application/javascript">
-
-/** Test for Bug 375008 **/
-
-/*
- * This test is divided in three parts:
- * - cases where focus isn't doable but blur should not happen;
- * - cases where focus is doable;
- * - cases where focus isn't doable but blur should still happen.
- */
-
-SimpleTest.waitForExplicitFinish();
-SimpleTest.waitForFocus(function() {
-  // On Mac, this preference needs to be turned on to be able to focus all the
-  // form controls we want to focus.
-  SpecialPowers.pushPrefEnv({"set": [[ "accessibility.mouse_focuses_formcontrol", true ]]},
-                            runTest);
-});
-
-function runTest()
-{
-  var witness = document.getElementById('witness');
-  witness.focus();
-
-  var notFocusableElements = document.getElementById('not-focusable').children;
-  for (var i=0; i<notFocusableElements.length; ++i) {
-    var element = notFocusableElements[i];
-    element.hidden = false;
-    synthesizeMouseAtCenter(element, {});
-    is(document.activeElement, witness,
-       "[" + element.tagName + "] witness should still be focused");
-
-    // Cleanup.
-    element.hidden = true;
-    witness.focus();
-  }
-
-  var focusableElements = document.getElementById('focusable').children;
-  for (var i=0; i<focusableElements.length; ++i) {
-    var element = focusableElements[i];
-    element.hidden = false;
-    synthesizeMouseAtCenter(element, {});
-    is(document.activeElement, element, "focus should have moved to " + element);
-
-    // Cleanup.
-    element.hidden = true;
-    witness.focus();
-  }
-
-  var hiddenElements = document.getElementById('hidden').children;
-  for (var i=0; i<hiddenElements.length; ++i) {
-    var element = hiddenElements[i];
-    element.hidden = false;
-    synthesizeMouseAtCenter(element, {});
-    is(document.activeElement, document.body,
-       "focus should have moved to the body");
-
-    // Cleanup.
-    element.hidden = true;
-    witness.focus();
-  }
-
-  SimpleTest.finish();
-}
-
-</script>
-</pre>
-</body>
-</html>