Skip to content

Commit 5849262

Browse files
committed
[JENKINS-76249] Fix form submission when a dropdown changes and the web response is slow
calling renderOnDemand can cause the dom to be manipulated aynchronouslyi due to function calls going to the Jenkins server (e.g. stapler/bound/xxx/render). When this occurs if there is a subsequent change to the dropdown selection before this completes, when the HTML is finally obtained from Jenkins it is inserted into the DOM, but it is inserted as if it is still the selected option (ie it is not disable for the form). renderOnDemand takes a callback function that will be called when the DOM manipulation is complete, however the updateDropDownList did not pass any function. We now pass a function that manipulates the just added HTML if the selected item is in fact no longer selected.
1 parent a7d3225 commit 5849262

File tree

1 file changed

+45
-26
lines changed

1 file changed

+45
-26
lines changed

war/src/main/webapp/scripts/hudson-behavior.js

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,47 +1683,66 @@ function rowvgStartEachRow(recursive, f) {
16831683
return;
16841684
}
16851685

1686-
var subForms = [];
1687-
var start = findInFollowingTR(e, "dropdownList-container");
1686+
function buildSubForms(e) {
1687+
var subForms = [];
1688+
var start = findInFollowingTR(e, "dropdownList-container");
16881689

1689-
do {
1690-
start = start.firstElementChild;
1691-
} while (start && !isTR(start));
1690+
do {
1691+
start = start.firstElementChild;
1692+
} while (start && !isTR(start));
16921693

1693-
if (start && !start.classList.contains("dropdownList-start")) {
1694-
start = findFollowingTR(start, "dropdownList-start");
1695-
}
1696-
while (start != null) {
1697-
subForms.push(start);
1698-
start = findFollowingTR(start, "dropdownList-start");
1694+
if (start && !start.classList.contains("dropdownList-start")) {
1695+
start = findFollowingTR(start, "dropdownList-start");
1696+
}
1697+
while (start != null) {
1698+
subForms.push(start);
1699+
start = findFollowingTR(start, "dropdownList-start");
1700+
}
1701+
return subForms;
16991702
}
17001703

1704+
var subForms = buildSubForms(e);
17011705
// control visibility
17021706
function updateDropDownList() {
17031707
for (var i = 0; i < subForms.length; i++) {
17041708
var show = e.selectedIndex == i;
17051709
var f = subForms[i];
17061710

17071711
if (show) {
1708-
renderOnDemand(f.nextElementSibling);
1712+
const idx = i; // capture the index so that it is not mutated in the loop
1713+
renderOnDemand(f.nextElementSibling, function () {
1714+
var current = e.selectedIndex == idx;
1715+
if (!current) {
1716+
console.log(
1717+
"**** renderOnDemandCallback, selection no longer valid, form submission would have been corrupted",
1718+
);
1719+
// our form div has changed (but the index is stable) so go and re-get the new domtree
1720+
var subForm = buildSubForms(e)[i];
1721+
updateDropDownFormRowVisibility(subForm, false);
1722+
}
1723+
});
17091724
}
1710-
f.rowVisibilityGroup.makeInnerVisible(show);
1711-
1712-
// TODO: this is actually incorrect in the general case if nested vg uses field-disabled
1713-
// so far dropdownList doesn't create such a situation.
1714-
f.rowVisibilityGroup.eachRow(
1715-
true,
1716-
show
1717-
? function (e) {
1718-
e.removeAttribute("field-disabled");
1719-
}
1720-
: function (e) {
1721-
e.setAttribute("field-disabled", "true");
1722-
},
1723-
);
1725+
updateDropDownFormRowVisibility(f, show);
17241726
}
17251727
}
17261728

1729+
function updateDropDownFormRowVisibility(f, show) {
1730+
f.rowVisibilityGroup.makeInnerVisible(show);
1731+
1732+
// TODO: this is actually incorrect in the general case if nested vg uses field-disabled
1733+
// so far dropdownList doesn't create such a situation.
1734+
f.rowVisibilityGroup.eachRow(
1735+
true,
1736+
show
1737+
? function (e) {
1738+
e.removeAttribute("field-disabled");
1739+
}
1740+
: function (e) {
1741+
e.setAttribute("field-disabled", "true");
1742+
},
1743+
);
1744+
}
1745+
17271746
e.onchange = updateDropDownList;
17281747

17291748
updateDropDownList();

0 commit comments

Comments
 (0)