diff --git a/src/scripting_api/aform.js b/src/scripting_api/aform.js index c91d72786f07f..747f503740576 100644 --- a/src/scripting_api/aform.js +++ b/src/scripting_api/aform.js @@ -486,15 +486,15 @@ class AForm { ]); function _checkValidity(_value, _cMask) { - for (let i = 0, ii = value.length; i < ii; i++) { - const mask = _cMask.charAt(i); - const char = _value.charAt(i); - const checker = checkers.get(mask); + for (let i = 0, ii = _value.length; i < ii; i++) { + const maskChar = _cMask.charAt(i); + const valueChar = _value.charAt(i); + const checker = checkers.get(maskChar); if (checker) { - if (!checker(char)) { + if (!checker(valueChar)) { return false; } - } else if (mask !== char) { + } else if (maskChar !== valueChar) { return false; } } @@ -564,7 +564,7 @@ class AForm { event.change.length + event.selStart - event.selEnd; - if (finalLen >= 8) { + if (finalLen > 8 || event.value[0] === "(") { formatStr = "(999) 999-9999"; } else { formatStr = "999-9999"; diff --git a/src/scripting_api/event.js b/src/scripting_api/event.js index a06fb0220b024..5b5bb082e75bc 100644 --- a/src/scripting_api/event.js +++ b/src/scripting_api/event.js @@ -47,11 +47,32 @@ class EventDispatcher { this._document.obj._eventDispatcher = this; } + /* + * Take an event object and reconstruct what we think the result will be once + * the change is applied, so we can validate against it and cancel the event + * if need be + * + * TODO: Given the event info we currently have, we can't determine where in + * the value a single-character insertion/deletion happened. For now we just + * assume they happen at the end of the string, which is by far the most + * common case, but this leaves some edge cases, see issue #14307. + */ mergeChange(event) { let value = event.value; if (typeof value !== "string") { value = value.toString(); } + // If there was no selection, it's a single-character change + if (event.selStart === -1 && event.selEnd === -1) { + if (event.change === "") { + // Empty change indicates a deletion + return value.slice(0, -1); + } + // Otherwise, assume it's an append + return value + event.change; + } + + // Otherwise, splice in the change to replace the selection const prefix = event.selStart >= 0 ? value.substring(0, event.selStart) : ""; const postfix = diff --git a/test/unit/scripting_spec.js b/test/unit/scripting_spec.js index c6236761b6023..46fe7d5f5dac3 100644 --- a/test/unit/scripting_spec.js +++ b/test/unit/scripting_spec.js @@ -361,15 +361,15 @@ describe("Scripting", function () { name: "Keystroke", willCommit: false, change: "o", - selStart: 4, - selEnd: 4, + selStart: -1, + selEnd: -1, }); expect(send_queue.has(refId)).toEqual(true); expect(send_queue.get(refId)).toEqual({ id: refId, value: "hell", - selRange: [4, 4], + selRange: [-1, -1], }); }); @@ -398,8 +398,8 @@ describe("Scripting", function () { name: "Keystroke", willCommit: false, change: "o", - selStart: 4, - selEnd: 4, + selStart: -1, + selEnd: -1, }); expect(send_queue.has(refId)).toEqual(true); @@ -1119,8 +1119,8 @@ describe("Scripting", function () { change: "3", name: "Keystroke", willCommit: false, - selStart: 0, - selEnd: 0, + selStart: -1, + selEnd: -1, }); expect(send_queue.has(refId)).toEqual(false); @@ -1130,8 +1130,8 @@ describe("Scripting", function () { change: "F", name: "Keystroke", willCommit: false, - selStart: 1, - selEnd: 1, + selStart: -1, + selEnd: -1, }); expect(send_queue.has(refId)).toEqual(false); @@ -1141,8 +1141,8 @@ describe("Scripting", function () { change: "?", name: "Keystroke", willCommit: false, - selStart: 2, - selEnd: 2, + selStart: -1, + selEnd: -1, }); expect(send_queue.has(refId)).toEqual(false); @@ -1152,14 +1152,14 @@ describe("Scripting", function () { change: "@", name: "Keystroke", willCommit: false, - selStart: 3, - selEnd: 3, + selStart: -1, + selEnd: -1, }); expect(send_queue.has(refId)).toEqual(true); expect(send_queue.get(refId)).toEqual({ id: refId, value: "3F?", - selRange: [3, 3], + selRange: [-1, -1], }); send_queue.delete(refId); @@ -1169,7 +1169,7 @@ describe("Scripting", function () { change: "0", name: "Keystroke", willCommit: true, - selStart: 3, + selStart: -1, selEnd: 3, }); expect(send_queue.has(refId)).toEqual(false);