зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1659217 - Ensure formLike.rootElement correctly points to the nearest HTMLFormElement ancestor, if any, when a password field is inside a ShadowRoot. r=MattN
Differential Revision: https://phabricator.services.mozilla.com/D87335
This commit is contained in:
Родитель
b68673dd69
Коммит
bbe50878af
|
@ -46,7 +46,7 @@ this.LoginFormFactory = {
|
|||
|
||||
/**
|
||||
* Maps all DOM content documents in this content process, including those in
|
||||
* frames, to a WeakSet of LoginForm for the document.
|
||||
* frames, to a WeakSet of LoginForm.rootElement for the document.
|
||||
*/
|
||||
_loginFormRootElementsByDocument: new WeakMap(),
|
||||
|
||||
|
@ -104,8 +104,10 @@ this.LoginFormFactory = {
|
|||
);
|
||||
}
|
||||
|
||||
if (aField.form) {
|
||||
return this.createFromForm(aField.form);
|
||||
let form =
|
||||
aField.form || FormLikeFactory.closestFormIgnoringShadowRoots(aField);
|
||||
if (form) {
|
||||
return this.createFromForm(form);
|
||||
} else if (aField.hasAttribute("form")) {
|
||||
log.debug(
|
||||
"createFromField: field has form attribute but no form: ",
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simple form with username and password fields together in a shadow root with a <form> ancestor -->
|
||||
<!-- This form is based off of toolkit/components/passwordmgr/test/browser/form_basic.html -->
|
||||
<form id="both-fields-together-in-a-shadow-root">
|
||||
<!-- username and password inputs generated programmatically below -->
|
||||
<input id="submit" type="submit">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const form = document.getElementById("both-fields-together-in-a-shadow-root");
|
||||
const submitButton = document.getElementById("submit");
|
||||
const wrapper = document.createElement("span");
|
||||
wrapper.id = "wrapper-un-and-pw";
|
||||
const shadow = wrapper.attachShadow({mode: "closed"});
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = field;
|
||||
inputEle.name = field;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
shadow.append(inputEle);
|
||||
}
|
||||
submitButton.before(wrapper);
|
||||
</script>
|
||||
|
||||
</body></html>
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simple form with form, username and password fields together in a shadow root -->
|
||||
<!-- This form is based off of toolkit/components/passwordmgr/test/browser/form_basic.html -->
|
||||
<span id="wrapper">
|
||||
<!-- form and all inputs generated programmatically below -->
|
||||
</span>
|
||||
|
||||
<script>
|
||||
const wrapper = document.getElementById("wrapper");
|
||||
const shadow = wrapper.attachShadow({mode: "closed"});
|
||||
const form = document.createElement("form");
|
||||
form.id = "form-and-fields-in-a-shadow-root";
|
||||
const submitButton = document.createElement("input");
|
||||
submitButton.id = "submit";
|
||||
submitButton.type = "submit";
|
||||
shadow.append(form);
|
||||
form.append(submitButton);
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = field;
|
||||
inputEle.name = field;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
submitButton.before(inputEle);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body></html>
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simple form with username and password fields together in nested shadow roots -->
|
||||
<!-- This form is based off of toolkit/components/passwordmgr/test/browser/form_basic.html -->
|
||||
<form id="each-field-its-own-shadow">
|
||||
<span id="outer-wrapper">
|
||||
<!-- username and password inputs generated programmatically below -->
|
||||
</span>
|
||||
<input id="submit" type="submit">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const submitButton = document.getElementById("submit");
|
||||
const innerWrapper = document.createElement("span");
|
||||
innerWrapper.id = "inner-wrapper";
|
||||
const innerShadow = innerWrapper.attachShadow({mode: "closed"});
|
||||
const outerWrapper = document.getElementById("outer-wrapper");
|
||||
const outerShadow = outerWrapper.attachShadow({mode: "closed"});
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = field;
|
||||
inputEle.name = field;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
innerShadow.append(inputEle);
|
||||
}
|
||||
outerShadow.append(innerWrapper);
|
||||
</script>
|
||||
|
||||
</body></html>
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simple form with form, username and password fields together in nested shadow roots -->
|
||||
<!-- This form is based off of toolkit/components/passwordmgr/test/browser/form_basic.html -->
|
||||
<span id="outer-wrapper">
|
||||
<!-- form and all inputs generated programmatically below -->
|
||||
</span>
|
||||
|
||||
<script>
|
||||
const outerWrapper = document.getElementById("outer-wrapper");
|
||||
const innerWrapper = document.createElement("span");
|
||||
innerWrapper.id = "inner-wrapper";
|
||||
const innerShadow = innerWrapper.attachShadow({mode: "closed"});
|
||||
const outerShadow = outerWrapper.attachShadow({mode: "closed"});
|
||||
const form = document.createElement("form");
|
||||
form.id = "form-and-fields-in-a-shadow-root";
|
||||
const submitButton = document.createElement("input");
|
||||
submitButton.id = "submit";
|
||||
submitButton.type = "submit";
|
||||
innerShadow.append(form);
|
||||
form.append(submitButton);
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = field;
|
||||
inputEle.name = field;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
submitButton.before(inputEle);
|
||||
}
|
||||
outerShadow.append(innerWrapper);
|
||||
</script>
|
||||
|
||||
</body></html>
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simple form with username and password fields together in a shadow root -->
|
||||
<!-- This form is based off of toolkit/components/passwordmgr/test/browser/formless_basic.html -->
|
||||
<!-- username and password inputs generated programmatically below -->
|
||||
<input id="submit" type="submit">
|
||||
|
||||
<script>
|
||||
const submitButton = document.getElementById("submit");
|
||||
const wrapper = document.createElement("span");
|
||||
wrapper.id = "wrapper-un-and-pw";
|
||||
const shadow = wrapper.attachShadow({mode: "closed"});
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = field;
|
||||
inputEle.name = field;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
shadow.append(inputEle);
|
||||
}
|
||||
submitButton.before(wrapper);
|
||||
</script>
|
||||
|
||||
</body></html>
|
|
@ -8,7 +8,6 @@
|
|||
<input id="submit" type="submit">
|
||||
|
||||
<script>
|
||||
const { body } = document;
|
||||
const submitButton = document.getElementById("submit");
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simple form with form, username and password fields together in a shadow root -->
|
||||
<!-- This form is based off of toolkit/components/passwordmgr/test/browser/formless_basic.html -->
|
||||
<span id="wrapper">
|
||||
</span>
|
||||
<!-- username, password and submit inputs generated programmatically below -->
|
||||
|
||||
<script>
|
||||
const wrapper = document.getElementById("wrapper");
|
||||
const shadow = wrapper.attachShadow({mode: "closed"});
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = field;
|
||||
inputEle.name = field;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
shadow.append(inputEle);
|
||||
}
|
||||
const submitButton = document.createElement("input");
|
||||
submitButton.id = "submit";
|
||||
submitButton.type = "submit";
|
||||
shadow.append(submitButton);
|
||||
</script>
|
||||
|
||||
</body></html>
|
|
@ -21,9 +21,16 @@ support-files =
|
|||
../browser/form_same_origin_action.html
|
||||
auth2/authenticate.sjs
|
||||
file_history_back.html
|
||||
form_basic_shadow_DOM_both_fields_together_in_a_shadow_root.html
|
||||
form_basic_shadow_DOM_each_field_in_its_own_shadow_root.html
|
||||
form_basic_shadow_DOM_form_and_fields_together_in_a_shadow_root.html
|
||||
form_nested_shadow_DOM_both_fields_together_in_a_shadow_root.html
|
||||
form_nested_shadow_DOM_each_field_in_its_own_shadow_root.html
|
||||
form_nested_shadow_DOM_form_and_fields_together_in_a_shadow_root.html
|
||||
formless_basic_shadow_DOM_both_fields_together_in_a_shadow_root.html
|
||||
formless_basic_shadow_DOM_each_field_in_its_own_shadow_root.html
|
||||
formless_basic_shadow_DOM_form_and_fields_together_in_a_shadow_root.html
|
||||
multiple_forms_shadow_DOM_all_known_variants.html
|
||||
pwmgr_common.js
|
||||
pwmgr_common_parent.js
|
||||
../authenticate.sjs
|
||||
|
@ -172,6 +179,9 @@ skip-if = toolkit == 'android' && debug # bug 1397615
|
|||
[test_formless_submit_navigation_negative.html]
|
||||
skip-if = toolkit == 'android' && debug # bug 1397615
|
||||
|| xorigin # Hangs
|
||||
[test_formLike_rootElement_with_Shadow_DOM.html]
|
||||
scheme = https
|
||||
skip-if = xorigin # Hangs
|
||||
[test_input_events.html]
|
||||
[test_input_events_for_identical_values.html]
|
||||
[test_LoginManagerContent_passwordEditedOrGenerated.html]
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Page with multiple forms containing the following Shadow DOM variants: -->
|
||||
<!-- Case 1: Each field (username and password) in its own shadow root -->
|
||||
<!-- Case 2: Both fields (username and password) together in a shadow root with a form ancestor -->
|
||||
<!-- Case 3: Form and fields (username and password) together in a shadow root -->
|
||||
<span id="outer-wrapper">
|
||||
</span>
|
||||
|
||||
<script>
|
||||
const outerWrapper = document.getElementById("outer-wrapper");
|
||||
const outerShadow = outerWrapper.attachShadow({mode: "closed"});
|
||||
|
||||
function makeFormlessOuterForm(scenario) {
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = `${field}-${scenario}`;
|
||||
inputEle.name = `${field}-${scenario}`;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
outerShadow.append(inputEle);
|
||||
}
|
||||
const submitButton = document.createElement("input");
|
||||
submitButton.id = `submit-${scenario}`;
|
||||
submitButton.type = "submit";
|
||||
outerShadow.append(submitButton);
|
||||
}
|
||||
|
||||
function makeFormEachFieldInItsOwnShadowRoot(scenario) {
|
||||
const form = document.createElement("form");
|
||||
form.id = scenario;
|
||||
const submitButton = document.createElement("input");
|
||||
submitButton.id = `submit-${scenario}`;
|
||||
submitButton.type = "submit";
|
||||
form.append(submitButton);
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = `${field}-${scenario}`;
|
||||
inputEle.name = `${field}-${scenario}`;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
const wrapper = document.createElement("span");
|
||||
wrapper.id = `wrapper-${field}-${scenario}`;
|
||||
const shadow = wrapper.attachShadow({mode: "closed"});
|
||||
shadow.append(inputEle);
|
||||
submitButton.before(wrapper);
|
||||
}
|
||||
outerShadow.append(form);
|
||||
}
|
||||
|
||||
function makeFormBothFieldsTogetherInAShadowRoot(scenario) {
|
||||
const form = document.createElement("form");
|
||||
form.id = scenario;
|
||||
const submitButton = document.createElement("input");
|
||||
submitButton.id = `submit-${scenario}`;
|
||||
submitButton.type = "submit";
|
||||
form.append(submitButton);
|
||||
const wrapper = document.createElement("span");
|
||||
wrapper.id = `wrapper-${scenario}`;
|
||||
const shadow = wrapper.attachShadow({mode: "closed"});
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = `${field}-${scenario}`;
|
||||
inputEle.name = `${field}-${scenario}`;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
shadow.append(inputEle);
|
||||
}
|
||||
submitButton.before(wrapper);
|
||||
outerShadow.append(form);
|
||||
}
|
||||
|
||||
function makeFormFormAndFieldsTogetherInAShadowRoot(scenario) {
|
||||
const wrapper = document.createElement("span");
|
||||
wrapper.id = `wrapper-${scenario}`;
|
||||
const shadow = wrapper.attachShadow({mode: "closed"});
|
||||
const form = document.createElement("form");
|
||||
form.id = scenario;
|
||||
shadow.append(form);
|
||||
const submitButton = document.createElement("input");
|
||||
submitButton.id = `submit-${scenario}`;
|
||||
submitButton.type = "submit";
|
||||
form.append(submitButton);
|
||||
const fields = ["username", "password"];
|
||||
for (let field of fields) {
|
||||
const inputEle = document.createElement("input");
|
||||
inputEle.id = field;
|
||||
inputEle.name = field;
|
||||
if (field === "password") {
|
||||
inputEle.type = field;
|
||||
}
|
||||
submitButton.before(inputEle);
|
||||
}
|
||||
outerShadow.append(wrapper);
|
||||
}
|
||||
|
||||
makeFormlessOuterForm("formless-case-2");
|
||||
makeFormEachFieldInItsOwnShadowRoot("form-case-1");
|
||||
makeFormBothFieldsTogetherInAShadowRoot("form-case-2");
|
||||
makeFormFormAndFieldsTogetherInAShadowRoot("form-case-3");
|
||||
</script>
|
||||
|
||||
</body></html>
|
|
@ -0,0 +1,149 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test that FormLike.rootElement points to right element when the page has Shadow DOM</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<iframe></iframe>
|
||||
|
||||
<script type="application/javascript">
|
||||
const { LoginFormFactory } = SpecialPowers.Cu.import("resource://gre/modules/LoginFormFactory.jsm", {});
|
||||
|
||||
add_task(async function setup() {
|
||||
const readyPromise = registerRunTests();
|
||||
info("Waiting for setup and page load");
|
||||
await readyPromise;
|
||||
|
||||
// assert that there are no logins
|
||||
const allLogins = await LoginManager.getAllLogins();
|
||||
is(allLogins.length, 0, "There are no logins");
|
||||
});
|
||||
|
||||
const IFRAME = document.querySelector("iframe");
|
||||
const TESTCASES = [
|
||||
// Check that the Shadow DOM version of form_basic.html works
|
||||
{
|
||||
name: "test_form_each_field_in_its_own_shadow_root",
|
||||
filename: "form_basic_shadow_DOM_each_field_in_its_own_shadow_root.html",
|
||||
hostAndRootElementSelectorTuples: [["span#wrapper-password", "form"]],
|
||||
},
|
||||
{
|
||||
name: "test_form_both_fields_together_in_a_shadow_root",
|
||||
filename: "form_basic_shadow_DOM_both_fields_together_in_a_shadow_root.html",
|
||||
hostAndRootElementSelectorTuples: [["span#wrapper-un-and-pw", "form"]],
|
||||
},
|
||||
{
|
||||
name: "test_form_form_and_fields_together_in_a_shadow_root",
|
||||
filename: "form_basic_shadow_DOM_form_and_fields_together_in_a_shadow_root.html",
|
||||
hostAndRootElementSelectorTuples: [["span#wrapper", "form"]],
|
||||
},
|
||||
// Check that the Shadow DOM version of formless_basic.html works
|
||||
{
|
||||
name: "test_formless_each_field_in_its_own_shadow_root",
|
||||
filename: "formless_basic_shadow_DOM_each_field_in_its_own_shadow_root.html",
|
||||
hostAndRootElementSelectorTuples: [["span#wrapper-password", "html"]],
|
||||
},
|
||||
{
|
||||
name: "test_formless_both_fields_together_in_a_shadow_root",
|
||||
filename: "formless_basic_shadow_DOM_both_fields_together_in_a_shadow_root.html",
|
||||
hostAndRootElementSelectorTuples: [["span#wrapper-un-and-pw", "html"]],
|
||||
},
|
||||
{
|
||||
name: "test_formless_form_and_fields_together_in_a_shadow_root.html",
|
||||
filename: "formless_basic_shadow_DOM_form_and_fields_together_in_a_shadow_root.html",
|
||||
hostAndRootElementSelectorTuples: [["span#wrapper", "html"]],
|
||||
},
|
||||
// Check that the nested Shadow DOM version of form_basic.html works
|
||||
{
|
||||
name: "test_form_nested_each_field_in_its_own_shadow_root",
|
||||
filename: "form_nested_shadow_DOM_each_field_in_its_own_shadow_root.html",
|
||||
hostAndRootElementSelectorTuples: [["span#wrapper-password", "form"]],
|
||||
outerHostElementSelector: "span#outer-wrapper-password",
|
||||
},
|
||||
{
|
||||
name: "test_form_nested_both_fields_together_in_a_shadow_root",
|
||||
filename: "form_nested_shadow_DOM_both_fields_together_in_a_shadow_root.html",
|
||||
hostAndRootElementSelectorTuples: [["span#inner-wrapper", "form"]],
|
||||
outerHostElementSelector: "span#outer-wrapper",
|
||||
},
|
||||
{
|
||||
name: "test_form_nested_form_and_fields_together_in_a_shadow_root",
|
||||
filename: "form_nested_shadow_DOM_form_and_fields_together_in_a_shadow_root.html",
|
||||
hostAndRootElementSelectorTuples: [["span#inner-wrapper", "form"]],
|
||||
outerHostElementSelector: "span#outer-wrapper",
|
||||
},
|
||||
{
|
||||
name: "test_multiple_forms_shadow_DOM_all_known_variants",
|
||||
filename: "multiple_forms_shadow_DOM_all_known_variants.html",
|
||||
hostAndRootElementSelectorTuples: [
|
||||
["span#outer-wrapper", "html"],
|
||||
["span#wrapper-password-form-case-1", "form#form-case-1"],
|
||||
["span#wrapper-form-case-2", "form#form-case-2"],
|
||||
["span#wrapper-form-case-3", "form#form-case-3"],
|
||||
],
|
||||
outerHostElementSelector: "span#outer-wrapper",
|
||||
}
|
||||
];
|
||||
|
||||
async function testForm(testcase) {
|
||||
const iframeLoaded = new Promise(resolve => {
|
||||
IFRAME.addEventListener(
|
||||
"load",
|
||||
function(e) {
|
||||
resolve(true);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
|
||||
// This could complete before the page finishes loading.
|
||||
const numForms = testcase.hostAndRootElementSelectorTuples.length;
|
||||
const formsProcessed = promiseFormsProcessed(numForms);
|
||||
|
||||
IFRAME.src = testcase.filename;
|
||||
info("Waiting for test page to load in the iframe");
|
||||
await iframeLoaded;
|
||||
|
||||
info(`Wait for ${numForms} form(s) to be processed.`);
|
||||
await formsProcessed;
|
||||
|
||||
const iframeDoc = SpecialPowers.wrap(IFRAME.contentWindow).document;
|
||||
for (let [hostElementSelector, rootElementSelector] of testcase.hostAndRootElementSelectorTuples) {
|
||||
info("Get the expected rootElement from the document");
|
||||
let hostElement = iframeDoc.querySelector(hostElementSelector);
|
||||
let outerShadowRoot = null;
|
||||
if (!hostElement) {
|
||||
// Nested Shadow DOM testcase
|
||||
const outerHostElement = iframeDoc.querySelector(testcase.outerHostElementSelector);
|
||||
outerShadowRoot = outerHostElement.openOrClosedShadowRoot;
|
||||
hostElement = outerShadowRoot.querySelector(hostElementSelector);
|
||||
}
|
||||
const shadowRoot = hostElement.openOrClosedShadowRoot;
|
||||
let expectedRootElement = iframeDoc.querySelector(rootElementSelector);
|
||||
if (!expectedRootElement) {
|
||||
// The form itself is inside a ShadowRoot and/or there is a ShadowRoot in between the field and form
|
||||
expectedRootElement =
|
||||
shadowRoot.querySelector(rootElementSelector) ||
|
||||
outerShadowRoot.querySelector(rootElementSelector);
|
||||
}
|
||||
ok(LoginFormFactory.getRootElementsWeakSetForDocument(iframeDoc).has(expectedRootElement), "Ensure formLike.rootElement has the expected value");
|
||||
}
|
||||
}
|
||||
|
||||
for (let testcase of TESTCASES) {
|
||||
const taskName = testcase.name;
|
||||
const tmp = {
|
||||
async [taskName]() {
|
||||
await testForm(testcase);
|
||||
}
|
||||
}
|
||||
add_task(tmp[taskName]);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -105,6 +105,27 @@ let FormLikeFactory = {
|
|||
return formLike;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the closest <form> if any when aField is inside a ShadowRoot.
|
||||
*
|
||||
* @param {HTMLInputElement} aField - a password or username field in a document
|
||||
* @return {HTMLFormElement|null}
|
||||
*/
|
||||
closestFormIgnoringShadowRoots(aField) {
|
||||
let form = aField.closest("form");
|
||||
let current = aField;
|
||||
while (!form) {
|
||||
let shadowRoot = current.getRootNode();
|
||||
if (ChromeUtils.getClassName(shadowRoot) !== "ShadowRoot") {
|
||||
break;
|
||||
}
|
||||
let host = shadowRoot.host;
|
||||
form = host.closest("form");
|
||||
current = host;
|
||||
}
|
||||
return form;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine the Element that encapsulates the related fields. For example, if
|
||||
* a page contains a login form and a checkout form which are "submitted"
|
||||
|
@ -116,8 +137,9 @@ let FormLikeFactory = {
|
|||
* @return {HTMLElement} - the root element surrounding related fields
|
||||
*/
|
||||
findRootForField(aField) {
|
||||
if (aField.form) {
|
||||
return aField.form;
|
||||
let form = aField.form || this.closestFormIgnoringShadowRoots(aField);
|
||||
if (form) {
|
||||
return form;
|
||||
}
|
||||
|
||||
return aField.ownerDocument.documentElement;
|
||||
|
|
Загрузка…
Ссылка в новой задаче