Bug 1489440 - Fix nsFormFillController in presence of Shadow DOM. r=smaug
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 21 Jan 2019 14:29:16 +0000
changeset 514757 0d9a334f6ae8011d712a69c751aabd81e31764c7
parent 514756 22b28921759054aa25d1bc4b78e3a2761af40d81
child 514758 605d15524a488ee41d9566f1f48904798cb188a8
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1489440
milestone66.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 1489440 - Fix nsFormFillController in presence of Shadow DOM. r=smaug Differential Revision: https://phabricator.services.mozilla.com/D17121
toolkit/components/satchel/nsFormFillController.cpp
toolkit/components/satchel/test/mochitest.ini
toolkit/components/satchel/test/test_datalist_shadow_dom.html
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -784,17 +784,17 @@ nsFormFillController::HandleEvent(Event*
       return Focus(aEvent);
     case eMouseDown:
       return MouseDown(aEvent);
     case eKeyDown:
       return KeyDown(aEvent);
     case eKeyPress:
       return KeyPress(aEvent);
     case eEditorInput: {
-      nsCOMPtr<nsINode> input = do_QueryInterface(aEvent->GetTarget());
+      nsCOMPtr<nsINode> input = do_QueryInterface(aEvent->GetComposedTarget());
       if (!IsTextControl(input)) {
         return NS_OK;
       }
 
       bool unused = false;
       return (!mSuppressOnInput && mController && mFocusedInput)
                  ? mController->HandleText(&unused)
                  : NS_OK;
@@ -922,17 +922,17 @@ void nsFormFillController::MaybeStartCon
   // password field.
   if (aInput->ControlType() == NS_FORM_INPUT_PASSWORD) {
     StartQueryLoginReputation(aInput);
   }
 #endif
 }
 
 nsresult nsFormFillController::Focus(Event* aEvent) {
-  nsCOMPtr<nsIContent> input = do_QueryInterface(aEvent->GetTarget());
+  nsCOMPtr<nsIContent> input = do_QueryInterface(aEvent->GetComposedTarget());
   MaybeStartControllingInput(HTMLInputElement::FromNodeOrNull(input));
 
   // Bail if we didn't start controlling the input.
   if (!mFocusedInput) {
     return NS_OK;
   }
 
 #ifndef ANDROID
@@ -1089,17 +1089,17 @@ nsresult nsFormFillController::KeyPress(
 }
 
 nsresult nsFormFillController::MouseDown(Event* aEvent) {
   MouseEvent* mouseEvent = aEvent->AsMouseEvent();
   if (!mouseEvent) {
     return NS_ERROR_FAILURE;
   }
 
-  nsCOMPtr<nsINode> targetNode = do_QueryInterface(aEvent->GetTarget());
+  nsCOMPtr<nsINode> targetNode = do_QueryInterface(aEvent->GetComposedTarget());
   if (!HTMLInputElement::FromNodeOrNull(targetNode)) {
     return NS_OK;
   }
 
   int16_t button = mouseEvent->Button();
 
   // In case of a right click we set a timestamp that
   // will be checked in Focus() to avoid showing
--- a/toolkit/components/satchel/test/mochitest.ini
+++ b/toolkit/components/satchel/test/mochitest.ini
@@ -6,16 +6,17 @@ support-files =
   subtst_privbrowsing.html
   parent_utils.js
 
 [test_bug_511615.html]
 skip-if = os == 'linux' # bug 1022386
 [test_bug_787624.html]
 skip-if = os == 'linux' # bug 1022386
 [test_datalist_with_caching.html]
+[test_datalist_shadow_dom.html]
 [test_form_autocomplete.html]
 skip-if = (verify && debug && (os == 'win')) || os == 'linux' # linux - bug 1022386
 [test_form_autocomplete_with_list.html]
 skip-if = os == 'linux' # bug 1022386
 [test_form_submission.html]
 [test_form_submission_cap.html]
 [test_form_submission_cap2.html]
 [test_password_autocomplete.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/satchel/test/test_datalist_shadow_dom.html
@@ -0,0 +1,124 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for datalist in Shadow DOM</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <script type="text/javascript" src="satchel_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+  <div id="host"></div>
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+const host = document.getElementById("host");
+host.attachShadow({ mode: "open" }).innerHTML = `
+  <form id="form1" onsubmit="return false;">
+    <input list="suggest" type="text" name="field1">
+    <button type="submit">Submit</button>
+  </form>
+  <datalist id="suggest">
+    <option value="First"></option>
+    <option value="Second"></option>
+    <option value="Secomundo"></option>
+  </datalist><Paste>
+`;
+
+let input = host.shadowRoot.querySelector("input");
+
+function setForm(value) {
+  input.value = value;
+  input.focus();
+}
+
+// Restore the form to the default state.
+function restoreForm() {
+  setForm("");
+}
+
+// Check for expected form data.
+function checkForm(expectedValue) {
+  let formID = input.parentNode.id;
+  is(input.value, expectedValue, "Checking " + formID + " input");
+}
+
+SimpleTest.waitForExplicitFinish();
+
+var expectingPopup = null;
+
+function expectPopup() {
+  info("expecting a popup");
+  return new Promise(resolve => {
+    expectingPopup = resolve;
+  });
+}
+
+var testNum = 0;
+
+function popupShownListener() {
+  info("popup shown for test " + testNum);
+  if (expectingPopup) {
+    expectingPopup();
+    expectingPopup = null;
+  } else {
+    ok(false, "Autocomplete popup not expected during test " + testNum);
+  }
+}
+
+function waitForMenuChange(expectedCount) {
+  return new Promise(resolve => {
+    notifyMenuChanged(expectedCount, null, resolve);
+  });
+}
+
+registerPopupShownListener(popupShownListener);
+
+function checkMenuEntries(expectedValues) {
+  let actualValues = getMenuEntries();
+  is(actualValues.length, expectedValues.length, testNum + " Checking length of expected menu");
+  for (let i = 0; i < expectedValues.length; i++) {
+    is(actualValues[i], expectedValues[i], testNum + " Checking menu entry #" + i);
+  }
+}
+
+async function runTests() {
+  testNum++;
+  restoreForm();
+  synthesizeKey("KEY_ArrowDown");
+  await expectPopup();
+
+  checkMenuEntries(["First", "Second", "Secomundo"]);
+  synthesizeKey("KEY_ArrowDown");
+  synthesizeKey("KEY_Enter");
+  checkForm("First");
+
+  testNum++;
+  restoreForm();
+  sendString("Sec");
+  synthesizeKey("KEY_ArrowDown");
+  await expectPopup();
+
+  testNum++;
+  checkMenuEntries(["Second", "Secomundo"]);
+  sendString("o");
+  await waitForMenuChange(2);
+
+  testNum++;
+  checkMenuEntries(["Second", "Secomundo"]);
+  synthesizeKey("KEY_ArrowDown");
+  synthesizeKey("KEY_Enter");
+  checkForm("Second");
+  SimpleTest.finish();
+}
+
+window.onload = runTests();
+
+</script>
+</pre>
+</body>
+</html>