зеркало из 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
|
* 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(),
|
_loginFormRootElementsByDocument: new WeakMap(),
|
||||||
|
|
||||||
|
@ -104,8 +104,10 @@ this.LoginFormFactory = {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aField.form) {
|
let form =
|
||||||
return this.createFromForm(aField.form);
|
aField.form || FormLikeFactory.closestFormIgnoringShadowRoots(aField);
|
||||||
|
if (form) {
|
||||||
|
return this.createFromForm(form);
|
||||||
} else if (aField.hasAttribute("form")) {
|
} else if (aField.hasAttribute("form")) {
|
||||||
log.debug(
|
log.debug(
|
||||||
"createFromField: field has form attribute but no form: ",
|
"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">
|
<input id="submit" type="submit">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const { body } = document;
|
|
||||||
const submitButton = document.getElementById("submit");
|
const submitButton = document.getElementById("submit");
|
||||||
const fields = ["username", "password"];
|
const fields = ["username", "password"];
|
||||||
for (let field of fields) {
|
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
|
../browser/form_same_origin_action.html
|
||||||
auth2/authenticate.sjs
|
auth2/authenticate.sjs
|
||||||
file_history_back.html
|
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_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_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_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.js
|
||||||
pwmgr_common_parent.js
|
pwmgr_common_parent.js
|
||||||
../authenticate.sjs
|
../authenticate.sjs
|
||||||
|
@ -172,6 +179,9 @@ skip-if = toolkit == 'android' && debug # bug 1397615
|
||||||
[test_formless_submit_navigation_negative.html]
|
[test_formless_submit_navigation_negative.html]
|
||||||
skip-if = toolkit == 'android' && debug # bug 1397615
|
skip-if = toolkit == 'android' && debug # bug 1397615
|
||||||
|| xorigin # Hangs
|
|| xorigin # Hangs
|
||||||
|
[test_formLike_rootElement_with_Shadow_DOM.html]
|
||||||
|
scheme = https
|
||||||
|
skip-if = xorigin # Hangs
|
||||||
[test_input_events.html]
|
[test_input_events.html]
|
||||||
[test_input_events_for_identical_values.html]
|
[test_input_events_for_identical_values.html]
|
||||||
[test_LoginManagerContent_passwordEditedOrGenerated.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;
|
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
|
* Determine the Element that encapsulates the related fields. For example, if
|
||||||
* a page contains a login form and a checkout form which are "submitted"
|
* 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
|
* @return {HTMLElement} - the root element surrounding related fields
|
||||||
*/
|
*/
|
||||||
findRootForField(aField) {
|
findRootForField(aField) {
|
||||||
if (aField.form) {
|
let form = aField.form || this.closestFormIgnoringShadowRoots(aField);
|
||||||
return aField.form;
|
if (form) {
|
||||||
|
return form;
|
||||||
}
|
}
|
||||||
|
|
||||||
return aField.ownerDocument.documentElement;
|
return aField.ownerDocument.documentElement;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче